diff --git a/backend/lcfs/conftest.py b/backend/lcfs/conftest.py index f6e63f32..69c244a6 100644 --- a/backend/lcfs/conftest.py +++ b/backend/lcfs/conftest.py @@ -1,10 +1,11 @@ import structlog import warnings + # Suppress the PendingDeprecationWarning for multipart warnings.filterwarnings( "ignore", message="Please use `import python_multipart` instead.", - category=PendingDeprecationWarning + category=PendingDeprecationWarning, ) import subprocess import warnings @@ -43,6 +44,7 @@ logger = structlog.get_logger(__name__) + @pytest.fixture(scope="session") def anyio_backend() -> str: """ diff --git a/backend/lcfs/tests/fuel_export/conftest.py b/backend/lcfs/tests/fuel_export/conftest.py index 746cc7f6..fdb99e06 100644 --- a/backend/lcfs/tests/fuel_export/conftest.py +++ b/backend/lcfs/tests/fuel_export/conftest.py @@ -1,6 +1,7 @@ import pytest from unittest.mock import AsyncMock, MagicMock from lcfs.web.api.fuel_export.repo import FuelExportRepository +from lcfs.web.api.fuel_code.repo import FuelCodeRepository from lcfs.web.api.fuel_export.services import FuelExportServices from lcfs.web.api.fuel_export.actions_service import FuelExportActionService from fastapi_cache import FastAPICache @@ -11,18 +12,20 @@ @pytest.fixture(scope="function", autouse=True) async def init_cache(): + """Initialize the cache for testing.""" FastAPICache.init(InMemoryBackend(), prefix="test-cache") @pytest.fixture def mock_db(): + """Mock the AsyncSession for database interactions.""" return AsyncMock(spec=AsyncSession) @pytest.fixture -def mock_repo(): - repo = AsyncMock(spec=FuelExportRepository()) - # Add specific async mock methods that need to be available +def mock_repo(mock_db): + """Mock FuelExportRepository.""" + repo = FuelExportRepository(db=mock_db) repo.get_fuel_export_table_options = AsyncMock() repo.get_fuel_export_list = AsyncMock() repo.get_fuel_exports_paginated = AsyncMock() @@ -38,17 +41,17 @@ def mock_repo(): @pytest.fixture def mock_compliance_report_repo(): - return AsyncMock(spec=ComplianceReportRepository) + """Mock ComplianceReportRepository.""" + repo = AsyncMock(spec=ComplianceReportRepository) + return repo @pytest.fixture -def fuel_export_service(mock_user_profile, mock_repo, mock_compliance_report_repo): - service = FuelExportServices() - service.repo = mock_repo - service.compliance_report_repo = mock_compliance_report_repo - service.request = MagicMock() - service.request.user = mock_user_profile - return service +def mock_fuel_code_repo(): + """Mock FuelCodeRepository.""" + repo = AsyncMock(spec=FuelCodeRepository) + repo.get_standardized_fuel_data = AsyncMock() + return repo @pytest.fixture @@ -59,23 +62,23 @@ def fuel_export_repo(mock_db): @pytest.fixture -def mock_fuel_export_services(): - services = AsyncMock(spec=FuelExportServices) - # Add specific methods that need to be available - services.validate_and_calculate_compliance_units = AsyncMock() - services.get_fuel_export_options = AsyncMock() - return services +def fuel_export_service(mock_repo, mock_compliance_report_repo): + """Mock FuelExportServices.""" + service = FuelExportServices( + repo=mock_repo, + compliance_report_repo=mock_compliance_report_repo, + ) + return service @pytest.fixture -def fuel_export_action_service(mock_repo, mock_fuel_export_services): - service = FuelExportActionService( - repo=mock_repo, fuel_export_services=mock_fuel_export_services - ) +def fuel_export_action_service(mock_repo, mock_fuel_code_repo): + """Mock FuelExportActionService.""" + service = FuelExportActionService(repo=mock_repo, fuel_repo=mock_fuel_code_repo) return service @pytest.fixture def mock_user_profile(): - """Mock user profile with minimal required attributes""" + """Mock user profile with minimal required attributes.""" return MagicMock(id=1, organization_id=1, user_type="SUPPLIER") diff --git a/backend/lcfs/tests/fuel_export/test_fuel_exports_actions_service.py b/backend/lcfs/tests/fuel_export/test_fuel_exports_actions_service.py new file mode 100644 index 00000000..d35801ad --- /dev/null +++ b/backend/lcfs/tests/fuel_export/test_fuel_exports_actions_service.py @@ -0,0 +1,519 @@ +import pytest +from unittest.mock import AsyncMock, MagicMock +from uuid import uuid4 +from datetime import datetime +from lcfs.db.models.compliance.FuelExport import FuelExport +from lcfs.db.models.compliance.ComplianceReport import QuantityUnitsEnum +from lcfs.web.api.fuel_export.actions_service import FuelExportActionService +from lcfs.web.api.fuel_export.schema import ( + FuelExportCreateUpdateSchema, + FuelExportSchema, + DeleteFuelExportResponseSchema, +) +from lcfs.db.base import ActionTypeEnum, UserTypeEnum +from lcfs.web.exception.exceptions import DatabaseException + +from fastapi import HTTPException + +FUEL_EXPORT_EXCLUDE_FIELDS = { + "id", + "fuel_export_id", + "compliance_period", + "deleted", + "group_uuid", + "user_type", + "version", + "action_type", + "units", +} + +# Example test cases from the dataset +test_cases = [ + { + "description": "Jet fuel, default carbon intensity, liters", + "input": { + "quantity": 100000, + "units": "L", + "target_ci": 88.83, + "ci_of_fuel": 88.83, + "energy_density": 36, + "eer": 1, + }, + "expected_compliance_units": 0, + }, + { + "description": "Diesel, prescribed carbon intensity, liters", + "input": { + "quantity": 100000, + "units": "L", + "target_ci": 79.28, + "ci_of_fuel": 94.38, + "energy_density": 38.65, + "eer": 1, + }, + "expected_compliance_units": 0, + }, + { + "description": "Gasoline, default carbon intensity, liters", + "input": { + "quantity": 100000, + "units": "L", + "target_ci": 78.68, + "ci_of_fuel": 93.67, + "energy_density": 34.69, + "eer": 1, + }, + "expected_compliance_units": 0, + }, + { + "description": "Diesel, fuel code, kWh", + "input": { + "quantity": 100000, + "units": "kWh", + "target_ci": 79.28, + "ci_of_fuel": 12.14, + "energy_density": 3.6, + "eer": 2.5, + }, + "expected_compliance_units": -67, + }, + { + "description": "Gasoline, default carbon intensity, m3", + "input": { + "quantity": 100000, + "units": "m3", + "target_ci": 78.68, + "ci_of_fuel": 63.91, + "energy_density": 38.27, + "eer": 0.9, + }, + "expected_compliance_units": -26, + }, +] + + +# Adjusted create_sample_fe_data function (if necessary) +def create_sample_fe_data(): + return FuelExportCreateUpdateSchema( + compliance_report_id=1, + fuel_type_id=1, + fuel_category_id=1, + end_use_id=1, + fuel_code_id=1, + compliance_period="2024", # Schema-only field + quantity=1000.0, + units="L", + energy_density=35.0, + group_uuid=str(uuid4()), + version=0, + provisionOfTheActId=123, + exportDate=datetime.now().date(), + ) + + +# Adjusted tests +@pytest.mark.anyio +async def test_create_fuel_export_success( + fuel_export_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = create_sample_fe_data() + user_type = UserTypeEnum.SUPPLIER + + # Mock the response from get_standardized_fuel_data + mock_fuel_code_repo.get_standardized_fuel_data.return_value = { + "effective_carbon_intensity": 50.0, + "target_ci": 80.0, + "eer": 1.0, + "energy_density": 35.0, + } + + fe_data.fuel_type_id = 3 + fe_data.fuel_category_id = 2 + fe_data.provision_of_the_act_id = 3 + + # Exclude invalid fields and set related objects + fe_data_dict = fe_data.model_dump(exclude=FUEL_EXPORT_EXCLUDE_FIELDS) + + # Mock the created fuel export + created_export = FuelExport( + **fe_data_dict, + ) + created_export.compliance_units = -100 + created_export.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + created_export.fuel_category = {"category": "Diesel"} + created_export.units = "Litres" + mock_repo.create_fuel_export.return_value = created_export + + # Call the method under test + result = await fuel_export_action_service.create_fuel_export(fe_data, user_type) + + # Assertions + assert result == FuelExportSchema.model_validate(created_export) + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once_with( + fuel_type_id=fe_data.fuel_type_id, + fuel_category_id=fe_data.fuel_category_id, + end_use_id=fe_data.end_use_id, + fuel_code_id=fe_data.fuel_code_id, + compliance_period=fe_data.compliance_period, + ) + mock_repo.create_fuel_export.assert_awaited_once() + # Ensure compliance units were calculated correctly + assert result.compliance_units < 0 + + +@pytest.mark.anyio +async def test_update_fuel_export_success_existing_report( + fuel_export_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = create_sample_fe_data() + user_type = UserTypeEnum.SUPPLIER + + # Exclude invalid fields + fe_data_dict = fe_data.model_dump(exclude=FUEL_EXPORT_EXCLUDE_FIELDS) + + # Mock existing export with matching compliance_report_id + existing_export = FuelExport( + **fe_data_dict, + fuel_export_id=1, + ) + existing_export.compliance_units = -100 + existing_export.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + existing_export.fuel_category = {"category": "Diesel"} + existing_export.units = "Litres" + mock_repo.get_fuel_export_version_by_user.return_value = existing_export + + # Mock the response from get_standardized_fuel_data + mock_fuel_code_repo.get_standardized_fuel_data.return_value = { + "effective_carbon_intensity": 55.0, + "target_ci": 85.0, + "eer": 1.2, + "energy_density": 36.0, + } + + # Mock the updated fuel export + updated_export = FuelExport( + **fe_data_dict, + fuel_export_id=1, + ) + updated_export.compliance_units = -150 + updated_export.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + updated_export.fuel_category = {"category": "Diesel"} + updated_export.units = "Litres" + mock_repo.update_fuel_export.return_value = updated_export + + # Call the method under test + result = await fuel_export_action_service.update_fuel_export(fe_data, user_type) + + # Assertions + assert result == FuelExportSchema.model_validate(updated_export) + mock_repo.get_fuel_export_version_by_user.assert_awaited_once_with( + fe_data.group_uuid, fe_data.version, user_type + ) + mock_repo.update_fuel_export.assert_awaited_once() + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once() + # Ensure compliance units were updated correctly + assert result.compliance_units == -150 + + +@pytest.mark.anyio +async def test_update_fuel_export_create_new_version( + fuel_export_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = create_sample_fe_data() + fe_data.compliance_report_id = 2 # Different compliance report ID + user_type = UserTypeEnum.SUPPLIER + + # Exclude invalid fields + fe_data_dict = fe_data.model_dump(exclude=FUEL_EXPORT_EXCLUDE_FIELDS) + + # Mock existing export with different compliance_report_id + existing_export = FuelExport( + **fe_data_dict, + fuel_export_id=1, + ) + existing_export.compliance_report_id = 1 # Original compliance_report_id + existing_export.compliance_units = -100 + existing_export.version = 0 + existing_export.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + existing_export.fuel_category = {"category": "Diesel"} + existing_export.units = "Litres" + mock_repo.get_fuel_export_version_by_user.return_value = existing_export + + # Mock the response from get_standardized_fuel_data + mock_fuel_code_repo.get_standardized_fuel_data.return_value = { + "effective_carbon_intensity": 60.0, + "target_ci": 90.0, + "eer": 1.5, + "energy_density": 37.0, + } + + # Mock the newly created export (new version) + new_export = FuelExport( + **fe_data_dict, + fuel_export_id=2, + version=existing_export.version + 1, + ) + new_export.compliance_units = -150 + new_export.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + new_export.fuel_category = {"category": "Diesel"} + new_export.units = "Litres" + mock_repo.create_fuel_export.return_value = new_export + + # Call the method under test + result = await fuel_export_action_service.update_fuel_export(fe_data, user_type) + + # Assertions + assert result == FuelExportSchema.model_validate(new_export) + mock_repo.get_fuel_export_version_by_user.assert_awaited_once_with( + fe_data.group_uuid, fe_data.version, user_type + ) + mock_repo.create_fuel_export.assert_awaited_once() + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once() + # Ensure compliance units were calculated correctly + assert result.compliance_units == -150 + + +@pytest.mark.anyio +async def test_delete_fuel_export_success( + fuel_export_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = create_sample_fe_data() + user_type = UserTypeEnum.SUPPLIER + + # Exclude invalid fields + fe_data_dict = fe_data.model_dump(exclude=FUEL_EXPORT_EXCLUDE_FIELDS) + + # Mock existing export + existing_export = FuelExport( + **fe_data_dict, + fuel_export_id=1, + version=0, + action_type=ActionTypeEnum.CREATE, + ) + existing_export.compliance_units = -100 + existing_export.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + existing_export.fuel_category = {"category": "Diesel"} + existing_export.units = "Litres" + mock_repo.get_latest_fuel_export_by_group_uuid.return_value = existing_export + + # Mock the deletion export + deleted_export = FuelExport( + **fe_data_dict, + fuel_export_id=2, + version=1, + action_type=ActionTypeEnum.DELETE, + ) + deleted_export.compliance_units = 0 + deleted_export.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + deleted_export.fuel_category = {"category": "Diesel"} + deleted_export.units = "Litres" + mock_repo.create_fuel_export.return_value = deleted_export + + # Call the method under test + result = await fuel_export_action_service.delete_fuel_export(fe_data, user_type) + + # Assertions + assert isinstance(result, DeleteFuelExportResponseSchema) + assert result.success is True + assert result.message == "Fuel export record marked as deleted." + mock_repo.get_latest_fuel_export_by_group_uuid.assert_awaited_once_with( + fe_data.group_uuid + ) + mock_repo.create_fuel_export.assert_awaited_once() + + +@pytest.mark.anyio +async def test_delete_fuel_export_already_deleted( + fuel_export_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = create_sample_fe_data() + user_type = UserTypeEnum.SUPPLIER + + # Exclude invalid fields + fe_data_dict = fe_data.model_dump(exclude=FUEL_EXPORT_EXCLUDE_FIELDS) + + # Mock existing export already marked as deleted + existing_export = FuelExport( + **fe_data_dict, + fuel_export_id=1, + version=1, + action_type=ActionTypeEnum.DELETE, + ) + existing_export.compliance_units = 0 + existing_export.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + existing_export.fuel_category = {"category": "Diesel"} + existing_export.units = "Litres" + mock_repo.get_latest_fuel_export_by_group_uuid.return_value = existing_export + + # Call the method under test + result = await fuel_export_action_service.delete_fuel_export(fe_data, user_type) + + # Assertions + assert isinstance(result, DeleteFuelExportResponseSchema) + assert result.success is True + assert result.message == "Fuel export record already deleted." + mock_repo.get_latest_fuel_export_by_group_uuid.assert_awaited_once_with( + fe_data.group_uuid + ) + mock_repo.create_fuel_export.assert_not_awaited() + + +@pytest.mark.anyio +async def test_populate_fuel_export_fields( + fuel_export_action_service, mock_fuel_code_repo +): + fe_data = create_sample_fe_data() + + # Exclude invalid fields + fe_data_dict = fe_data.model_dump(exclude=FUEL_EXPORT_EXCLUDE_FIELDS) + + # Create a FuelExport instance without populated fields + fuel_export = FuelExport(**fe_data_dict) + + # Mock standardized fuel data + mock_fuel_code_repo.get_standardized_fuel_data.return_value = { + "effective_carbon_intensity": 50.0, + "target_ci": 80.0, + "eer": None, # Test default EER + "energy_density": None, # Should use fe_data.energy_density + } + + # Call the method under test + populated_export = await fuel_export_action_service._populate_fuel_export_fields( + fuel_export, fe_data + ) + + # Assertions + assert populated_export.units == QuantityUnitsEnum(fe_data.units) + assert populated_export.ci_of_fuel == 50.0 + assert populated_export.target_ci == 80.0 + assert populated_export.eer == 1 # Default EER + assert populated_export.energy_density == fe_data.energy_density + # Energy calculation + assert populated_export.energy == round(fe_data.energy_density * fe_data.quantity) + # Compliance units calculation (should be negative) + assert populated_export.compliance_units < 0 + + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once_with( + fuel_type_id=fuel_export.fuel_type_id, + fuel_category_id=fuel_export.fuel_category_id, + end_use_id=fuel_export.end_use_id, + fuel_code_id=fuel_export.fuel_code_id, + compliance_period=fe_data.compliance_period, + ) + + +@pytest.mark.anyio +@pytest.mark.parametrize("case", test_cases) +async def test_compliance_units_calculation( + case, fuel_export_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = FuelExportCreateUpdateSchema( + compliance_report_id=1, + fuel_type_id=3, + fuel_category_id=2, + end_use_id=1, + fuel_code_id=1, + compliance_period="2024", # Schema-only field + quantity=case["input"]["quantity"], + units=case["input"]["units"], + energy_density=case["input"]["energy_density"], + group_uuid=str(uuid4()), + version=0, + provisionOfTheActId=123, + exportDate=datetime.now().date(), + ) + + # Mock standardized fuel data + mock_fuel_code_repo.get_standardized_fuel_data.return_value = { + "effective_carbon_intensity": case["input"]["ci_of_fuel"], + "target_ci": case["input"]["target_ci"], + "eer": case["input"]["eer"], + "energy_density": case["input"]["energy_density"], + } + + # Exclude invalid fields and set related objects + fe_data_dict = fe_data.model_dump(exclude=FUEL_EXPORT_EXCLUDE_FIELDS) + + # Define the side_effect function to simulate repository behavior + async def create_fuel_export_side_effect(fuel_export: FuelExport): + """ + Simulate the behavior of the repository's create_fuel_export method. + This function populates necessary fields that the service would normally handle. + """ + # Assign a mock fuel_export_id + fuel_export.fuel_export_id = 1 # or any unique identifier as needed + + # Simulate populating related objects + fuel_export.fuel_type = { + "fuel_type_id": fuel_export.fuel_type_id, + "fuel_type": "Electricity", + "units": "kWh", + } + fuel_export.fuel_category = {"category": "Diesel"} + fuel_export.units = "Litres" + + # The service should have already calculated compliance_units + return fuel_export + + # Set the side_effect for create_fuel_export + mock_repo.create_fuel_export.side_effect = create_fuel_export_side_effect + + # Call the service to create the fuel export + result = await fuel_export_action_service.create_fuel_export( + fe_data, UserTypeEnum.SUPPLIER + ) + + # Assign mocked related objects for schema validation + result.fuel_type = { + "fuel_type_id": fe_data.fuel_type_id, + "fuel_type": "Electricity", + "units": "kWh", + } + result.fuel_category = {"category": "Diesel"} + result.units = fe_data.units + + # Assertions + assert ( + result.compliance_units == case["expected_compliance_units"] + ), f"Failed {case['description']}. Expected {case['expected_compliance_units']}, got {result.compliance_units}" + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once_with( + fuel_type_id=fe_data.fuel_type_id, + fuel_category_id=fe_data.fuel_category_id, + end_use_id=fe_data.end_use_id, + fuel_code_id=fe_data.fuel_code_id, + compliance_period=fe_data.compliance_period, + ) + mock_repo.create_fuel_export.assert_awaited_once() diff --git a/backend/lcfs/tests/fuel_export/test_fuel_exports_repo.py b/backend/lcfs/tests/fuel_export/test_fuel_exports_repo.py index e6e76011..3850fd34 100644 --- a/backend/lcfs/tests/fuel_export/test_fuel_exports_repo.py +++ b/backend/lcfs/tests/fuel_export/test_fuel_exports_repo.py @@ -222,9 +222,9 @@ async def test_update_fuel_export_success(fuel_export_repo, mock_db): assert result == updated_fuel_export mock_db.merge.assert_called_once_with(fuel_export) - mock_db.flush.assert_called_once() + mock_db.flush.assert_awaited_once() mock_db.refresh.assert_awaited_once_with( - fuel_export, + updated_fuel_export, [ "fuel_category", "fuel_type", diff --git a/backend/lcfs/tests/fuel_export/test_fuel_exports_services.py b/backend/lcfs/tests/fuel_export/test_fuel_exports_services.py index 32dcf6eb..92d35bd4 100644 --- a/backend/lcfs/tests/fuel_export/test_fuel_exports_services.py +++ b/backend/lcfs/tests/fuel_export/test_fuel_exports_services.py @@ -99,9 +99,7 @@ async def test_get_fuel_exports_paginated_success(fuel_export_service, mock_repo # FuelExportActionService Tests @pytest.mark.anyio -async def test_action_create_fuel_export_success( - fuel_export_action_service, mock_repo, mock_fuel_export_services -): +async def test_action_create_fuel_export_success(fuel_export_action_service, mock_repo): input_data = FuelExportCreateUpdateSchema( compliance_report_id=1, fuel_type_id=1, @@ -132,27 +130,17 @@ async def test_action_create_fuel_export_success( fuel_type=mock_fuel_type.dict(), fuel_category=mock_fuel_category.dict(), ) - - mock_fuel_export_services.validate_and_calculate_compliance_units.return_value = ( - input_data - ) mock_repo.create_fuel_export.return_value = mock_created_export result = await fuel_export_action_service.create_fuel_export( input_data, UserTypeEnum.SUPPLIER ) - assert isinstance(result, FuelExportSchema) - mock_fuel_export_services.validate_and_calculate_compliance_units.assert_called_once_with( - input_data - ) mock_repo.create_fuel_export.assert_called_once() @pytest.mark.anyio -async def test_action_update_fuel_export_success( - fuel_export_action_service, mock_repo, mock_fuel_export_services -): +async def test_action_update_fuel_export_success(fuel_export_action_service, mock_repo): input_data = FuelExportCreateUpdateSchema( fuel_export_id=1, compliance_report_id=1, @@ -186,21 +174,13 @@ async def test_action_update_fuel_export_success( fuel_type=mock_fuel_type.dict(), fuel_category=mock_fuel_category.dict(), ) - - mock_fuel_export_services.validate_and_calculate_compliance_units.return_value = ( - input_data - ) mock_repo.get_fuel_export_version_by_user.return_value = mock_existing_export mock_repo.update_fuel_export.return_value = mock_existing_export result = await fuel_export_action_service.update_fuel_export( input_data, UserTypeEnum.SUPPLIER ) - assert isinstance(result, FuelExportSchema) - mock_fuel_export_services.validate_and_calculate_compliance_units.assert_called_once_with( - input_data - ) mock_repo.get_fuel_export_version_by_user.assert_called_once() mock_repo.update_fuel_export.assert_called_once() diff --git a/backend/lcfs/tests/fuel_supply/test_fuel_supplies_actions_service.py b/backend/lcfs/tests/fuel_supply/test_fuel_supplies_actions_service.py new file mode 100644 index 00000000..47d92b2e --- /dev/null +++ b/backend/lcfs/tests/fuel_supply/test_fuel_supplies_actions_service.py @@ -0,0 +1,618 @@ +# test_fuel_supply.py + +import pytest +from unittest.mock import AsyncMock, MagicMock +from uuid import uuid4 +from datetime import datetime +from lcfs.db.models.compliance.FuelSupply import FuelSupply +from lcfs.db.models.compliance.ComplianceReport import QuantityUnitsEnum +from lcfs.web.api.fuel_supply.actions_service import FuelSupplyActionService +from lcfs.web.api.fuel_supply.schema import ( + FuelSupplyCreateUpdateSchema, + FuelSupplyResponseSchema, + DeleteFuelSupplyResponseSchema, +) +from lcfs.db.base import ActionTypeEnum, UserTypeEnum +from lcfs.web.exception.exceptions import DatabaseException +from lcfs.web.utils.calculations import calculate_compliance_units + +from fastapi import HTTPException + +# Constants defining which fields to exclude during model operations +FUEL_SUPPLY_EXCLUDE_FIELDS = { + "id", + "fuel_supply_id", + "compliance_period", + "deleted", + "group_uuid", + "user_type", + "version", + "action_type", + "units", +} + +# Example test cases from the dataset +test_cases = [ + { + "description": "Jet fuel, default carbon intensity, liters", + "input": { + "quantity": 100000, + "units": "L", + "target_ci": 88.83, + "ci_of_fuel": 88.83, + "energy_density": 36, + "eer": 1, + }, + "expected_compliance_units": 0, + }, + { + "description": "Diesel, prescribed carbon intensity, liters", + "input": { + "quantity": 100000, + "units": "L", + "target_ci": 79.28, + "ci_of_fuel": 94.38, + "energy_density": 38.65, + "eer": 1, + }, + "expected_compliance_units": -58, + }, + { + "description": "Gasoline, default carbon intensity, liters", + "input": { + "quantity": 100000, + "units": "L", + "target_ci": 78.68, + "ci_of_fuel": 93.67, + "energy_density": 34.69, + "eer": 1, + }, + "expected_compliance_units": -52, + }, + { + "description": "Diesel, fuel code, kWh", + "input": { + "quantity": 100000, + "units": "kWh", + "target_ci": 79.28, + "ci_of_fuel": 12.14, + "energy_density": 3.6, + "eer": 2.5, + }, + "expected_compliance_units": 67, + }, + { + "description": "Gasoline, default carbon intensity, m3", + "input": { + "quantity": 100000, + "units": "m3", + "target_ci": 78.68, + "ci_of_fuel": 63.91, + "energy_density": 38.27, + "eer": 0.9, + }, + "expected_compliance_units": 26, + }, +] + + +# Fixtures for mocks +@pytest.fixture +def mock_repo(): + """Mock FuelSupplyRepository.""" + repo = AsyncMock() + repo.create_fuel_supply = AsyncMock() + repo.update_fuel_supply = AsyncMock() + repo.get_fuel_supply_version_by_user = AsyncMock() + repo.get_latest_fuel_supply_by_group_uuid = AsyncMock() + return repo + + +@pytest.fixture +def mock_fuel_code_repo(): + """Mock FuelCodeRepository.""" + fuel_code_repo = AsyncMock() + fuel_code_repo.get_standardized_fuel_data = AsyncMock() + return fuel_code_repo + + +@pytest.fixture +def fuel_supply_action_service(mock_repo, mock_fuel_code_repo): + """Instantiate the FuelSupplyActionService with mocked dependencies.""" + return FuelSupplyActionService(repo=mock_repo, fuel_repo=mock_fuel_code_repo) + + +# Helper function to create sample FuelSupplyCreateUpdateSchema +def create_sample_fs_data(): + return FuelSupplyCreateUpdateSchema( + compliance_report_id=1, + fuel_type_id=1, + fuel_category_id=1, + end_use_id=1, + fuel_code_id=1, + compliance_period="2024", # Schema-only field + quantity=1000.0, + units="L", + energy_density=35.0, + group_uuid=str(uuid4()), + version=0, + provisionOfTheActId=123, + exportDate=datetime.now().date(), + ) + + +# Adjusted tests +@pytest.mark.anyio +async def test_create_fuel_supply_success( + fuel_supply_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = create_sample_fs_data() + user_type = UserTypeEnum.SUPPLIER + + # Mock the response from get_standardized_fuel_data + mock_fuel_code_repo.get_standardized_fuel_data.return_value = { + "effective_carbon_intensity": 50.0, + "target_ci": 80.0, + "eer": 1.0, + "energy_density": 35.0, + } + + fe_data.fuel_type_id = 3 + fe_data.fuel_category_id = 2 + fe_data.provision_of_the_act_id = 3 + + # Exclude invalid fields and set related objects + fe_data_dict = fe_data.model_dump(exclude=FUEL_SUPPLY_EXCLUDE_FIELDS) + + # Mock the created fuel supply + created_supply = FuelSupply( + **fe_data_dict, + fuel_supply_id=1, + version=0, + group_uuid="test_uuid", + user_type=UserTypeEnum.SUPPLIER, + action_type=ActionTypeEnum.UPDATE, + ) + created_supply.compliance_units = -100 + created_supply.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + created_supply.fuel_category = {"category": "Diesel"} + created_supply.units = "Litres" + mock_repo.create_fuel_supply.return_value = created_supply + + # Call the method under test + result = await fuel_supply_action_service.create_fuel_supply(fe_data, user_type) + + # Assign mocked related objects for schema validation + result.fuel_type = { + "fuel_type_id": fe_data.fuel_type_id, + "fuel_type": "Electricity", + "units": "kWh", + } + result.fuel_category = {"category": "Diesel"} + result.units = fe_data.units + + # Assertions + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once_with( + fuel_type_id=fe_data.fuel_type_id, + fuel_category_id=fe_data.fuel_category_id, + end_use_id=fe_data.end_use_id, + fuel_code_id=fe_data.fuel_code_id, + compliance_period=fe_data.compliance_period, + ) + mock_repo.create_fuel_supply.assert_awaited_once() + # Ensure compliance units were calculated correctly + assert result.compliance_units < 0 + + +@pytest.mark.anyio +async def test_update_fuel_supply_success_existing_report( + fuel_supply_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = create_sample_fs_data() + user_type = UserTypeEnum.SUPPLIER + + # Exclude invalid fields + fe_data_dict = fe_data.model_dump(exclude=FUEL_SUPPLY_EXCLUDE_FIELDS) + + # Mock existing supply with matching compliance_report_id + existing_supply = FuelSupply( + **fe_data_dict, + fuel_supply_id=1, + version=0, + group_uuid="test_uuid", + user_type=UserTypeEnum.SUPPLIER, + action_type=ActionTypeEnum.UPDATE, + ) + existing_supply.compliance_units = -100 + existing_supply.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + existing_supply.fuel_category = {"category": "Diesel"} + existing_supply.units = "Litres" + mock_repo.get_fuel_supply_version_by_user.return_value = existing_supply + + # Mock the response from get_standardized_fuel_data + mock_fuel_code_repo.get_standardized_fuel_data.return_value = { + "effective_carbon_intensity": 55.0, + "target_ci": 85.0, + "eer": 1.2, + "energy_density": 36.0, + } + + # Mock the updated fuel supply + updated_supply = FuelSupply( + **fe_data_dict, + fuel_supply_id=1, + version=1, + group_uuid="test_uuid", + user_type=UserTypeEnum.SUPPLIER, + action_type=ActionTypeEnum.UPDATE, + ) + updated_supply.compliance_units = -150 + updated_supply.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + updated_supply.fuel_category = {"category": "Diesel"} + updated_supply.units = "Litres" + mock_repo.update_fuel_supply.return_value = updated_supply + + # Call the method under test + result = await fuel_supply_action_service.update_fuel_supply(fe_data, user_type) + + # Assign mocked related objects for schema validation + result.fuel_type = { + "fuel_type_id": fe_data.fuel_type_id, + "fuel_type": "Electricity", + "units": "kWh", + } + result.fuel_category = {"category": "Diesel"} + result.units = fe_data.units + + # Assertions + mock_repo.get_fuel_supply_version_by_user.assert_awaited_once_with( + fe_data.group_uuid, fe_data.version, user_type + ) + mock_repo.update_fuel_supply.assert_awaited_once() + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once() + # Ensure compliance units were updated correctly + assert result.compliance_units == -150 + + +@pytest.mark.anyio +async def test_update_fuel_supply_create_new_version( + fuel_supply_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = create_sample_fs_data() + fe_data.compliance_report_id = 2 # Different compliance report ID + user_type = UserTypeEnum.SUPPLIER + + # Exclude invalid fields + fe_data_dict = fe_data.model_dump(exclude=FUEL_SUPPLY_EXCLUDE_FIELDS) + + # Mock existing supply with different compliance_report_id + existing_supply = FuelSupply( + **fe_data_dict, + fuel_supply_id=1, + version=0, + group_uuid="test_uuid", + user_type=UserTypeEnum.SUPPLIER, + action_type=ActionTypeEnum.CREATE, + ) + existing_supply.compliance_report_id = 1 # Original compliance_report_id + existing_supply.compliance_units = -100 + existing_supply.version = 0 + existing_supply.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + existing_supply.fuel_category = {"category": "Diesel"} + existing_supply.units = "Litres" + mock_repo.get_fuel_supply_version_by_user.return_value = existing_supply + + # Mock the response from get_standardized_fuel_data + mock_fuel_code_repo.get_standardized_fuel_data.return_value = { + "effective_carbon_intensity": 60.0, + "target_ci": 90.0, + "eer": 1.5, + "energy_density": 37.0, + } + + # Mock the newly created supply (new version) + new_supply = FuelSupply( + **fe_data_dict, + fuel_supply_id=2, + version=existing_supply.version + 1, + group_uuid="test_uuid", + user_type=UserTypeEnum.SUPPLIER, + action_type=ActionTypeEnum.UPDATE, + ) + new_supply.compliance_units = -150 + new_supply.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + new_supply.fuel_category = {"category": "Diesel"} + new_supply.units = "Litres" + mock_repo.create_fuel_supply.return_value = new_supply + + # Call the method under test + result = await fuel_supply_action_service.update_fuel_supply(fe_data, user_type) + + # Assign mocked related objects for schema validation + result.fuel_type = { + "fuel_type_id": fe_data.fuel_type_id, + "fuel_type": "Electricity", + "units": "kWh", + } + result.fuel_category = {"category": "Diesel"} + result.units = fe_data.units + + # Assertions + mock_repo.get_fuel_supply_version_by_user.assert_awaited_once_with( + fe_data.group_uuid, fe_data.version, user_type + ) + mock_repo.create_fuel_supply.assert_awaited_once() + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once() + # Ensure compliance units were calculated correctly + assert result.compliance_units == -150 + + +@pytest.mark.anyio +async def test_delete_fuel_supply_success( + fuel_supply_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = create_sample_fs_data() + user_type = UserTypeEnum.SUPPLIER + + # Exclude invalid fields + fe_data_dict = fe_data.model_dump(exclude=FUEL_SUPPLY_EXCLUDE_FIELDS) + + # Mock existing supply + existing_supply = FuelSupply( + **fe_data_dict, + fuel_supply_id=1, + version=0, + action_type=ActionTypeEnum.CREATE, + ) + existing_supply.compliance_units = -100 + existing_supply.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + existing_supply.fuel_category = {"category": "Diesel"} + existing_supply.units = "Litres" + mock_repo.get_latest_fuel_supply_by_group_uuid.return_value = existing_supply + + # Mock the deletion supply + deleted_supply = FuelSupply( + **fe_data_dict, + fuel_supply_id=2, + version=1, + action_type=ActionTypeEnum.DELETE, + ) + deleted_supply.compliance_units = 0 + deleted_supply.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + deleted_supply.fuel_category = {"category": "Diesel"} + deleted_supply.units = "Litres" + mock_repo.create_fuel_supply.return_value = deleted_supply + + # Call the method under test + result = await fuel_supply_action_service.delete_fuel_supply(fe_data, user_type) + + # Assertions + assert isinstance(result, DeleteFuelSupplyResponseSchema) + assert result.success is True + assert result.message == "Marked as deleted." + mock_repo.get_latest_fuel_supply_by_group_uuid.assert_awaited_once_with( + fe_data.group_uuid + ) + mock_repo.create_fuel_supply.assert_awaited_once() + + +@pytest.mark.anyio +async def test_delete_fuel_supply_already_deleted( + fuel_supply_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = create_sample_fs_data() + user_type = UserTypeEnum.SUPPLIER + + # Exclude invalid fields + fe_data_dict = fe_data.model_dump(exclude=FUEL_SUPPLY_EXCLUDE_FIELDS) + + # Mock existing supply already marked as deleted + existing_supply = FuelSupply( + **fe_data_dict, + fuel_supply_id=1, + version=1, + action_type=ActionTypeEnum.DELETE, + ) + existing_supply.compliance_units = 0 + existing_supply.fuel_type = { + "fuel_type_id": 3, + "fuel_type": "Electricity", + "units": "kWh", + } + existing_supply.fuel_category = {"category": "Diesel"} + existing_supply.units = "Litres" + mock_repo.get_latest_fuel_supply_by_group_uuid.return_value = existing_supply + + # Call the method under test + result = await fuel_supply_action_service.delete_fuel_supply(fe_data, user_type) + + # Assertions + assert isinstance(result, DeleteFuelSupplyResponseSchema) + assert result.success is True + assert result.message == "Already deleted." + mock_repo.get_latest_fuel_supply_by_group_uuid.assert_awaited_once_with( + fe_data.group_uuid + ) + mock_repo.create_fuel_supply.assert_not_awaited() + + +@pytest.mark.anyio +async def test_populate_fuel_supply_fields( + fuel_supply_action_service, mock_fuel_code_repo +): + fe_data = create_sample_fs_data() + + # Exclude invalid fields + fe_data_dict = fe_data.model_dump(exclude=FUEL_SUPPLY_EXCLUDE_FIELDS) + + # Create a FuelSupply instance without populated fields + fuel_supply = FuelSupply(**fe_data_dict) + + # Mock standardized fuel data + mock_fuel_code_repo.get_standardized_fuel_data.return_value = { + "effective_carbon_intensity": 50.0, + "target_ci": 80.0, + "eer": None, # Test default EER + "energy_density": None, # Should use fe_data.energy_density + } + + # Call the method under test + populated_supply = await fuel_supply_action_service._populate_fuel_supply_fields( + fuel_supply, fe_data + ) + + # Assertions + assert populated_supply.units == QuantityUnitsEnum(fe_data.units) + assert populated_supply.ci_of_fuel == 50.0 + assert populated_supply.target_ci == 80.0 + assert populated_supply.eer == 1 # Default EER + assert populated_supply.energy_density == fe_data.energy_density + # Energy calculation + assert populated_supply.energy == round(fe_data.energy_density * fe_data.quantity) + assert populated_supply.compliance_units > 0 + + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once_with( + fuel_type_id=fuel_supply.fuel_type_id, + fuel_category_id=fuel_supply.fuel_category_id, + end_use_id=fuel_supply.end_use_id, + fuel_code_id=fuel_supply.fuel_code_id, + compliance_period=fe_data.compliance_period, + ) + + +@pytest.mark.anyio +@pytest.mark.parametrize("case", test_cases) +async def test_compliance_units_calculation( + case, fuel_supply_action_service, mock_repo, mock_fuel_code_repo +): + fe_data = FuelSupplyCreateUpdateSchema( + compliance_report_id=1, + fuel_type_id=3, # Adjusted to match the mock fuel_type + fuel_category_id=2, # Adjusted to match the mock fuel_category + end_use_id=1, + fuel_code_id=1, + compliance_period="2024", # Schema-only field + quantity=case["input"]["quantity"], + units=case["input"]["units"], + energy_density=case["input"]["energy_density"], + group_uuid=str(uuid4()), + version=0, + provisionOfTheActId=123, + exportDate=datetime.now().date(), + ) + + # Mock standardized fuel data + mock_fuel_code_repo.get_standardized_fuel_data.return_value = { + "effective_carbon_intensity": case["input"]["ci_of_fuel"], + "target_ci": case["input"]["target_ci"], + "eer": case["input"]["eer"], + "energy_density": case["input"]["energy_density"], + } + + # Exclude invalid fields and set related objects + fe_data_dict = fe_data.model_dump(exclude=FUEL_SUPPLY_EXCLUDE_FIELDS) + + # Mock the create_fuel_supply method to perform actual calculation + async def create_fuel_supply_side_effect(fuel_supply): + fuel_supply.fuel_supply_id = 1 + # Simulate the _populate_fuel_supply_fields logic + fuel_supply.fuel_type = { + "fuel_type_id": fuel_supply.fuel_type_id, + "fuel_type": "Electricity", + "units": "kWh", + } + fuel_supply.fuel_category = {"category": "Diesel"} + fuel_supply.units = "Litres" + return fuel_supply + + mock_repo.create_fuel_supply.side_effect = create_fuel_supply_side_effect + + # Call the service to create the fuel supply + result = await fuel_supply_action_service.create_fuel_supply( + fe_data, UserTypeEnum.SUPPLIER + ) + + # Assign mocked related objects for schema validation + result.fuel_type = { + "fuel_type_id": fe_data.fuel_type_id, + "fuel_type": "Electricity", + "units": "kWh", + } + result.fuel_category = {"category": "Diesel"} + result.units = fe_data.units + + # Assertions + assert ( + result.compliance_units == case["expected_compliance_units"] + ), f"Failed {case['description']}. Expected {case['expected_compliance_units']}, got {result.compliance_units}" + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once_with( + fuel_type_id=fe_data.fuel_type_id, + fuel_category_id=fe_data.fuel_category_id, + end_use_id=fe_data.end_use_id, + fuel_code_id=fe_data.fuel_code_id, + compliance_period=fe_data.compliance_period, + ) + mock_repo.create_fuel_supply.assert_awaited_once() + + +@pytest.mark.parametrize("case", test_cases) +def test_calculate_compliance_units(case): + """ + Test the calculate_compliance_units function with various input scenarios. + + Args: + case (dict): A dictionary containing the test case description, input parameters, and expected output. + """ + # Extract input parameters + quantity = case["input"]["quantity"] + units = case["input"]["units"] # Not used in calculation but included for context + target_ci = case["input"]["target_ci"] + ci_of_fuel = case["input"]["ci_of_fuel"] + energy_density = case["input"]["energy_density"] + eer = case["input"]["eer"] + + # Constants not provided in test_cases + UCI = 0 # Assuming Additional Carbon Intensity Attributable to Use is zero + + # Call the function under test + result = calculate_compliance_units( + TCI=target_ci, + EER=eer, + RCI=ci_of_fuel, + UCI=UCI, + Q=quantity, + ED=energy_density, + ) + + # Assert that the result matches the expected compliance units + assert ( + result == case["expected_compliance_units"] + ), f"Failed {case['description']}. Expected {case['expected_compliance_units']}, got {result}" diff --git a/backend/lcfs/tests/fuel_supply/test_fuel_supplies_services.py b/backend/lcfs/tests/fuel_supply/test_fuel_supplies_services.py index 360c601f..bfe03cfb 100644 --- a/backend/lcfs/tests/fuel_supply/test_fuel_supplies_services.py +++ b/backend/lcfs/tests/fuel_supply/test_fuel_supplies_services.py @@ -240,14 +240,12 @@ async def test_update_fuel_supply_success(fuel_supply_action_service): mock_repo.get_fuel_supply_version_by_user.assert_awaited_once_with( fs_data.group_uuid, fs_data.version, user_type ) - mock_fuel_code_repo.get_fuel_type_by_id.assert_awaited_once_with( - fs_data.fuel_type_id - ) - mock_fuel_code_repo.get_energy_effectiveness_ratio.assert_awaited_once_with( - fs_data.fuel_type_id, fs_data.fuel_category_id, fs_data.end_use_id - ) - mock_fuel_code_repo.get_energy_density.assert_awaited_once_with( - fs_data.fuel_type_id + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once_with( + fuel_type_id=fs_data.fuel_type_id, + fuel_category_id=fs_data.fuel_category_id, + end_use_id=fs_data.end_use_id, + fuel_code_id=fs_data.fuel_code_id, + compliance_period=fs_data.compliance_period, ) mock_repo.update_fuel_supply.assert_awaited_once_with(existing_fuel_supply) @@ -295,6 +293,7 @@ async def test_create_fuel_supply(fuel_supply_action_service): provisionOfTheAct={"provisionOfTheActId": 1, "name": "Act Provision"}, endUseType={"endUseTypeId": 1, "type": "Transport", "subType": "Personal"}, units="L", + compliancePeriod="2024", ) ) mock_fuel_code_repo.get_fuel_type_by_id = AsyncMock( @@ -315,11 +314,13 @@ async def test_create_fuel_supply(fuel_supply_action_service): assert isinstance(response, FuelSupplyResponseSchema) mock_repo.create_fuel_supply.assert_awaited_once() - mock_fuel_code_repo.get_fuel_type_by_id.assert_awaited_once_with( - fs_data.fuel_type_id + mock_fuel_code_repo.get_standardized_fuel_data.assert_awaited_once_with( + fuel_type_id=fs_data.fuel_type_id, + fuel_category_id=fs_data.fuel_category_id, + end_use_id=fs_data.end_use_id, + fuel_code_id=fs_data.fuel_code_id, + compliance_period=fs_data.compliance_period, ) - mock_fuel_code_repo.get_energy_effectiveness_ratio.assert_awaited_once() - mock_fuel_code_repo.get_energy_density.assert_awaited_once() @pytest.mark.anyio diff --git a/backend/lcfs/web/api/fuel_code/repo.py b/backend/lcfs/web/api/fuel_code/repo.py index 5a2cd286..4ec8f89a 100644 --- a/backend/lcfs/web/api/fuel_code/repo.py +++ b/backend/lcfs/web/api/fuel_code/repo.py @@ -1,6 +1,6 @@ import structlog from datetime import date -from typing import List, Dict, Any, Union +from typing import List, Dict, Any, Union, Optional from fastapi import Depends from lcfs.db.dependencies import get_async_db_session @@ -17,12 +17,14 @@ from lcfs.db.models.fuel.FinishedFuelTransportMode import FinishedFuelTransportMode from lcfs.db.models.fuel.EnergyDensity import EnergyDensity from lcfs.db.models.fuel.EnergyEffectivenessRatio import EnergyEffectivenessRatio +from lcfs.db.models.fuel.TargetCarbonIntensity import TargetCarbonIntensity from lcfs.db.models.fuel.AdditionalCarbonIntensity import AdditionalCarbonIntensity from lcfs.db.models.fuel.FuelCodeStatus import FuelCodeStatus, FuelCodeStatusEnum from lcfs.db.models.fuel.FuelCode import FuelCode from lcfs.db.models.fuel.UnitOfMeasure import UnitOfMeasure from lcfs.db.models.fuel.ExpectedUseType import ExpectedUseType from lcfs.db.models.fuel.ProvisionOfTheAct import ProvisionOfTheAct +from lcfs.db.models.compliance.CompliancePeriod import CompliancePeriod from lcfs.web.api.base import PaginationRequestSchema from lcfs.web.api.fuel_code.schema import FuelCodeCloneSchema, FuelCodeSchema from lcfs.web.core.decorators import repo_handler @@ -56,11 +58,9 @@ async def get_formatted_fuel_types(self) -> List[Dict[str, Any]]: # Define the filtering conditions for fuel codes current_date = date.today() fuel_code_filters = or_( - FuelCode.effective_date == None, - FuelCode.effective_date <= current_date + FuelCode.effective_date == None, FuelCode.effective_date <= current_date ) & or_( - FuelCode.expiration_date == None, - FuelCode.expiration_date > current_date + FuelCode.expiration_date == None, FuelCode.expiration_date > current_date ) # Build the query with filtered fuel_codes @@ -713,7 +713,6 @@ async def get_provision_of_the_act_by_name( ) return result.scalar_one_or_none() - @repo_handler async def get_energy_effectiveness_ratio( self, fuel_type_id: int, fuel_category_id: int, end_use_type_id: int @@ -728,3 +727,86 @@ async def get_energy_effectiveness_ratio( energy_density = result.scalars().first() return energy_density + + @repo_handler + async def get_target_carbon_intensities( + self, fuel_category_id: int, compliance_period: str + ) -> List[TargetCarbonIntensity]: + + compliance_period_id_subquery = ( + select(CompliancePeriod.compliance_period_id) + .where(CompliancePeriod.description == compliance_period) + .scalar_subquery() + ) + + stmt = ( + select(TargetCarbonIntensity) + .where( + TargetCarbonIntensity.fuel_category_id == fuel_category_id, + TargetCarbonIntensity.compliance_period_id + == compliance_period_id_subquery, + ) + .options( + joinedload(TargetCarbonIntensity.fuel_category), + joinedload(TargetCarbonIntensity.compliance_period), + ) + ) + result = await self.db.execute(stmt) + return result.scalars().all() + + @repo_handler + async def get_standardized_fuel_data( + self, + fuel_type_id: int, + fuel_category_id: int, + end_use_id: int, + compliance_period: str, + fuel_code_id: Optional[int] = None, + ): + """ + Fetch and standardize fuel data values required for compliance calculations. + """ + # Fetch the fuel type details + fuel_type = await self.get_fuel_type_by_id(fuel_type_id) + if not fuel_type: + raise ValueError("Invalid fuel type ID") + + # Determine energy density + energy_density = ( + (await self.get_energy_density(fuel_type_id)).density + if fuel_type.fuel_type != "Other" + else None + ) + + # Set effective carbon intensity and target carbon intensity + if fuel_code_id: + fuel_code = await self.get_fuel_code(fuel_code_id) + effective_carbon_intensity = fuel_code.carbon_intensity + else: + effective_carbon_intensity = fuel_type.default_carbon_intensity + + # Get energy effectiveness ratio (EER) + eer = None + if fuel_type_id and fuel_category_id and end_use_id: + energy_effectiveness = await self.get_energy_effectiveness_ratio( + fuel_type_id, fuel_category_id, end_use_id + ) + eer = energy_effectiveness.ratio if energy_effectiveness else 1 + + # Fetch target carbon intensity (TCI) + target_ci = None + target_carbon_intensities = await self.get_target_carbon_intensities( + fuel_category_id, compliance_period + ) + if target_carbon_intensities: + target_ci = next( + (tci.target_carbon_intensity for tci in target_carbon_intensities), + 0, + ) + + return { + "effective_carbon_intensity": effective_carbon_intensity, + "target_ci": target_ci, + "eer": eer, + "energy_density": energy_density, + } diff --git a/backend/lcfs/web/api/fuel_export/actions_service.py b/backend/lcfs/web/api/fuel_export/actions_service.py index bcdb033d..8adc2d98 100644 --- a/backend/lcfs/web/api/fuel_export/actions_service.py +++ b/backend/lcfs/web/api/fuel_export/actions_service.py @@ -5,13 +5,14 @@ from lcfs.db.base import ActionTypeEnum, UserTypeEnum from lcfs.db.models.compliance.FuelExport import FuelExport +from lcfs.db.models.compliance.ComplianceReport import QuantityUnitsEnum from lcfs.web.api.fuel_export.repo import FuelExportRepository +from lcfs.web.api.fuel_code.repo import FuelCodeRepository from lcfs.web.api.fuel_export.schema import ( DeleteFuelExportResponseSchema, FuelExportCreateUpdateSchema, FuelExportSchema, ) -from lcfs.web.api.fuel_export.services import FuelExportServices from lcfs.web.core.decorators import service_handler from lcfs.web.utils.calculations import calculate_compliance_units @@ -20,17 +21,14 @@ # Constants defining which fields to exclude during model operations FUEL_EXPORT_EXCLUDE_FIELDS = { "id", - "fuel_type", - "fuel_category", + "fuel_export_id", "compliance_period", - "provision_of_the_act", - "end_use", - "fuel_code", "deleted", "group_uuid", "user_type", "version", "action_type", + "units", } @@ -44,10 +42,56 @@ class FuelExportActionService: def __init__( self, repo: FuelExportRepository = Depends(), - fuel_export_services: FuelExportServices = Depends(), + fuel_repo: FuelCodeRepository = Depends(), ) -> None: self.repo = repo - self.fuel_export_services = fuel_export_services + self.fuel_repo = fuel_repo + + async def _populate_fuel_export_fields( + self, fuel_export: FuelExport, fe_data: FuelExportCreateUpdateSchema + ) -> FuelExport: + """ + Populate additional calculated and referenced fields for a FuelExport instance. + """ + # Fetch standardized fuel data + fuel_data = await self.fuel_repo.get_standardized_fuel_data( + fuel_type_id=fuel_export.fuel_type_id, + fuel_category_id=fuel_export.fuel_category_id, + end_use_id=fuel_export.end_use_id, + fuel_code_id=fuel_export.fuel_code_id, + compliance_period=fe_data.compliance_period, + ) + + fuel_export.units = QuantityUnitsEnum(fe_data.units) + fuel_export.ci_of_fuel = fuel_data["effective_carbon_intensity"] + fuel_export.target_ci = fuel_data["target_ci"] + fuel_export.eer = fuel_data["eer"] or 1 # Default EER to 1 if None + fuel_export.energy_density = ( + fuel_data["energy_density"] or fe_data.energy_density + ) + + # Calculate total energy if energy density is available + fuel_export.energy = ( + round(fuel_export.energy_density * fuel_export.quantity) + if fuel_export.energy_density + else 0 + ) + + # Calculate compliance units using the direct utility function + compliance_units = calculate_compliance_units( + TCI=fuel_export.target_ci or 0, + EER=fuel_export.eer or 1, + RCI=fuel_export.ci_of_fuel or 0, + UCI=0, # Assuming Additional Carbon Intensity Attributable to Use is zero + Q=fuel_export.quantity or 0, + ED=fuel_export.energy_density or 0, + ) + + # Adjust compliance units to negative to represent exports + compliance_units = -round(compliance_units) + fuel_export.compliance_units = compliance_units if compliance_units < 0 else 0 + + return fuel_export @service_handler async def create_fuel_export( @@ -62,13 +106,7 @@ async def create_fuel_export( Returns the newly created fuel export record as a response schema. """ - # Validate and calculate compliance units - fe_data = ( - await self.fuel_export_services.validate_and_calculate_compliance_units( - fe_data - ) - ) - + # Assign a unique group UUID for the new fuel export new_group_uuid = str(uuid.uuid4()) fuel_export = FuelExport( **fe_data.model_dump(exclude=FUEL_EXPORT_EXCLUDE_FIELDS), @@ -78,6 +116,10 @@ async def create_fuel_export( action_type=ActionTypeEnum.CREATE, ) + # Populate calculated and referenced fields + fuel_export = await self._populate_fuel_export_fields(fuel_export, fe_data) + + # Save the populated fuel export record created_export = await self.repo.create_fuel_export(fuel_export) return FuelExportSchema.model_validate(created_export) @@ -89,33 +131,63 @@ async def update_fuel_export( Update an existing fuel export record or create a new version if necessary. - Checks if a record exists for the given `group_uuid` and `version`. - - If a matching record exists, updates it; otherwise, creates a new version. + - If `compliance_report_id` matches, updates the existing record. + - If `compliance_report_id` differs, creates a new version. + - If no existing record is found, raises an HTTPException. Returns the updated or new version of the fuel export record. """ - # Validate and calculate compliance units - fe_data = ( - await self.fuel_export_services.validate_and_calculate_compliance_units( - fe_data - ) - ) - existing_export = await self.repo.get_fuel_export_version_by_user( fe_data.group_uuid, fe_data.version, user_type ) - if existing_export: - # Update existing record + if ( + existing_export + and existing_export.compliance_report_id == fe_data.compliance_report_id + ): + # Update existing record if compliance report ID matches for field, value in fe_data.model_dump( exclude=FUEL_EXPORT_EXCLUDE_FIELDS ).items(): + print(field, value) setattr(existing_export, field, value) + # Populate calculated fields + existing_export = await self._populate_fuel_export_fields( + existing_export, fe_data + ) + updated_export = await self.repo.update_fuel_export(existing_export) return FuelExportSchema.model_validate(updated_export) - else: - # Create a new version - return await self._create_new_version(fe_data, user_type) + + elif existing_export: + # Create a new version if compliance report ID differs + fuel_export = FuelExport( + compliance_report_id=fe_data.compliance_report_id, + group_uuid=fe_data.group_uuid, + version=existing_export.version + 1, + action_type=ActionTypeEnum.UPDATE, + user_type=user_type, + ) + + # Copy existing fields, then apply new data + for field in existing_export.__table__.columns.keys(): + if field not in FUEL_EXPORT_EXCLUDE_FIELDS: + setattr(fuel_export, field, getattr(existing_export, field)) + + for field, value in fe_data.model_dump( + exclude=FUEL_EXPORT_EXCLUDE_FIELDS + ).items(): + setattr(fuel_export, field, value) + + # Populate calculated fields + fuel_export = await self._populate_fuel_export_fields(fuel_export, fe_data) + + # Save the new version + new_export = await self.repo.create_fuel_export(fuel_export) + return FuelExportSchema.model_validate(new_export) + + raise HTTPException(status_code=404, detail="Fuel export record not found.") @service_handler async def delete_fuel_export( @@ -126,11 +198,11 @@ async def delete_fuel_export( Returns a response schema confirming deletion. """ - latest_export = await self.repo.get_latest_fuel_export_by_group_uuid( + existing_export = await self.repo.get_latest_fuel_export_by_group_uuid( fe_data.group_uuid ) - if latest_export and latest_export.action_type == ActionTypeEnum.DELETE: + if existing_export and existing_export.action_type == ActionTypeEnum.DELETE: return DeleteFuelExportResponseSchema( success=True, message="Fuel export record already deleted." ) @@ -139,57 +211,20 @@ async def delete_fuel_export( delete_export = FuelExport( compliance_report_id=fe_data.compliance_report_id, group_uuid=fe_data.group_uuid, - version=(latest_export.version + 1) if latest_export else 0, + version=(existing_export.version + 1) if existing_export else 0, action_type=ActionTypeEnum.DELETE, user_type=user_type, ) # Copy over necessary fields from the latest version - if latest_export: - for field in latest_export.__table__.columns.keys(): + if existing_export: + for field in existing_export.__table__.columns.keys(): if field not in FUEL_EXPORT_EXCLUDE_FIELDS: - setattr(delete_export, field, getattr(latest_export, field)) + setattr(delete_export, field, getattr(existing_export, field)) + + delete_export.units = QuantityUnitsEnum(fe_data.units) await self.repo.create_fuel_export(delete_export) return DeleteFuelExportResponseSchema( success=True, message="Fuel export record marked as deleted." ) - - async def _create_new_version( - self, - fe_data: FuelExportCreateUpdateSchema, - user_type: UserTypeEnum, - ) -> FuelExportSchema: - """ - Helper to create a new version of a fuel export record. - """ - latest_export = await self.repo.get_latest_fuel_export_by_group_uuid( - fe_data.group_uuid - ) - - # If there is no existing export in previous versions, raise an error - if not latest_export: - raise HTTPException( - status_code=404, detail="Fuel export not found in previous versions." - ) - - # Create the new version by copying fields and applying updates - fuel_export = FuelExport( - compliance_report_id=fe_data.compliance_report_id, - group_uuid=fe_data.group_uuid, - version=latest_export.version + 1, - action_type=ActionTypeEnum.UPDATE, - user_type=user_type, - ) - for field in latest_export.__table__.columns.keys(): - if field not in FUEL_EXPORT_EXCLUDE_FIELDS: - setattr(fuel_export, field, getattr(latest_export, field)) - - for field, value in fe_data.model_dump( - exclude=FUEL_EXPORT_EXCLUDE_FIELDS - ).items(): - setattr(fuel_export, field, value) - - # Save the new version - new_export = await self.repo.create_fuel_export(fuel_export) - return FuelExportSchema.model_validate(new_export) diff --git a/backend/lcfs/web/api/fuel_export/repo.py b/backend/lcfs/web/api/fuel_export/repo.py index 8e2ab690..917ced49 100644 --- a/backend/lcfs/web/api/fuel_export/repo.py +++ b/backend/lcfs/web/api/fuel_export/repo.py @@ -236,7 +236,7 @@ async def update_fuel_export(self, fuel_export: FuelExport) -> FuelExport: updated_fuel_export = self.db.merge(fuel_export) await self.db.flush() await self.db.refresh( - fuel_export, + updated_fuel_export, [ "fuel_category", "fuel_type", diff --git a/backend/lcfs/web/api/fuel_export/schema.py b/backend/lcfs/web/api/fuel_export/schema.py index 009f0499..6865038e 100644 --- a/backend/lcfs/web/api/fuel_export/schema.py +++ b/backend/lcfs/web/api/fuel_export/schema.py @@ -171,15 +171,10 @@ class FuelExportCreateUpdateSchema(BaseSchema): action_type: Optional[str] = None compliance_period: Optional[str] = None fuel_type_other: Optional[str] = None - fuel_type: Union[str, FuelTypeSchema] fuel_type_id: int - fuel_category: Union[str, FuelCategoryResponseSchema] fuel_category_id: int - end_use_type: Optional[Union[str, EndUseTypeSchema]] = None end_use_id: Optional[int] = None - provision_of_the_act: Union[str, ProvisionOfTheActSchema] provision_of_the_act_id: int - fuel_code: Optional[Union[str, FuelCodeSchema]] = None fuel_code_id: Optional[int] = None quantity: int = Field(..., gt=0) units: str diff --git a/backend/lcfs/web/api/fuel_export/services.py b/backend/lcfs/web/api/fuel_export/services.py index e2608c44..9a4e2f77 100644 --- a/backend/lcfs/web/api/fuel_export/services.py +++ b/backend/lcfs/web/api/fuel_export/services.py @@ -273,113 +273,3 @@ async def get_fuel_exports_paginated( ), fuel_exports=[FuelExportSchema.model_validate(fs) for fs in fuel_exports], ) - - async def validate_and_calculate_compliance_units( - self, fs_data: FuelExportSchema - ) -> FuelExportSchema: - """Validate and update the compliance units""" - - fs_data.units = QuantityUnitsEnum(fs_data.units) - - # Fetch fuel export options based on the compliance period - fuel_export_options = await self.get_fuel_export_options( - fs_data.compliance_period - ) - - # Extract the relevant fuel type data - fuel_type_data = next( - ( - obj - for obj in fuel_export_options.fuel_types - if fs_data.fuel_type == obj.fuel_type - ), - None, - ) - - if not fuel_type_data: - # Handle the case where the fuel type is not found - raise ValueError( - f"Fuel type {fs_data.fuel_type} not found in export options." - ) - # Get energy density - energy_density = ( - fuel_type_data.energy_density.energy_density - if fuel_type_data.fuel_type != "Other" - else fs_data.energy_density - ) - - # Get target carbon intensity (TCI) - target_ci = next( - ( - item.target_carbon_intensity - for item in fuel_type_data.target_carbon_intensities - if item.fuel_category.fuel_category == fs_data.fuel_category - ), - 0, - ) - - # Determine the recorded carbon intensity (RCI) - if "Fuel code" in fs_data.provision_of_the_act: - # Use fuel code carbon intensity - effective_carbon_intensity = next( - ( - item["fuelCodeCarbonIntensity"] - for item in fuel_type_data.get("fuelCodes", []) - if item["fuelCode"] == fs_data.fuel_code - ), - 0, - ) - else: - # Use default carbon intensity - effective_carbon_intensity = fuel_type_data.default_carbon_intensity - - # Get Energy Effectiveness Ratio (EER) - eer = next( - ( - item.energy_effectiveness_ratio - for item in fuel_type_data.eer_ratios - if item.fuel_category.fuel_category == fs_data.fuel_category - and ( - item.end_use_type is None - or item["endUseType"]["type"] == fs_data.end_use - ) - ), - 0, - ) - - # Ensure all values are floats and handle None values - target_ci = float(target_ci or 0) - eer = float(eer or 0) - effective_carbon_intensity = float(effective_carbon_intensity or 0) - energy_density = float(energy_density or 0) - quantity = float(fs_data.quantity or 0) - uci = 0 # Assuming Additional Carbon Intensity Attributable to Use is zero - - # Calculate compliance units using the shared utility function - compliance_units = calculate_compliance_units( - TCI=target_ci, - EER=eer, - RCI=effective_carbon_intensity, - UCI=uci, - Q=quantity, - ED=energy_density, - ) - - # Adjust compliance units to negative to act as exporting fuel - compliance_units = -compliance_units - - # Ensure compliance units are rounded and remain negative - compliance_units = round(compliance_units) if compliance_units < 0 else 0 - - # Calculate energy content - energy_content = round(energy_density * quantity) - - # Update the fs_data object with calculated values - fs_data.target_ci = target_ci - fs_data.ci_of_fuel = effective_carbon_intensity - fs_data.energy_density = energy_density - fs_data.energy = energy_content - fs_data.eer = eer - fs_data.compliance_units = compliance_units - - return fs_data diff --git a/backend/lcfs/web/api/fuel_supply/actions_service.py b/backend/lcfs/web/api/fuel_supply/actions_service.py index 291b88b2..6d8891f9 100644 --- a/backend/lcfs/web/api/fuel_supply/actions_service.py +++ b/backend/lcfs/web/api/fuel_supply/actions_service.py @@ -5,7 +5,8 @@ from fastapi import Depends, HTTPException from lcfs.db.base import ActionTypeEnum, UserTypeEnum -from lcfs.db.models.compliance.FuelSupply import FuelSupply, QuantityUnitsEnum +from lcfs.db.models.compliance.FuelSupply import FuelSupply +from lcfs.db.models.compliance.ComplianceReport import QuantityUnitsEnum from lcfs.web.api.fuel_code.repo import FuelCodeRepository from lcfs.web.api.fuel_supply.repo import FuelSupplyRepository from lcfs.web.api.fuel_supply.schema import ( @@ -22,11 +23,13 @@ FUEL_SUPPLY_EXCLUDE_FIELDS = { "id", "fuel_supply_id", + "compliance_period", "deleted", "group_uuid", "user_type", "version", "action_type", + "units", } @@ -51,81 +54,52 @@ async def _populate_fuel_supply_fields( """ Populate additional calculated and referenced fields for a FuelSupply instance. - This method: - - Sets unit types - - Populates default and overridden carbon intensities (CI) - - Calculates energy density, effectiveness ratios, and compliance units + Args: + fuel_supply (FuelSupply): The FuelSupply instance to populate. + fs_data (FuelSupplyCreateUpdateSchema): The data provided for creation or update. - Returns the updated FuelSupply object with calculated fields populated. + Returns: + FuelSupply: The populated FuelSupply instance. """ - # Set quantity units + # Fetch standardized fuel data + fuel_data = await self.fuel_repo.get_standardized_fuel_data( + fuel_type_id=fuel_supply.fuel_type_id, + fuel_category_id=fuel_supply.fuel_category_id, + end_use_id=fuel_supply.end_use_id, + fuel_code_id=fuel_supply.fuel_code_id, + compliance_period=fs_data.compliance_period, + ) + + # Set units fuel_supply.units = QuantityUnitsEnum(fs_data.units) - # Get the fuel type details to check if it's unrecognized and set basic properties - fuel_type = await self.fuel_repo.get_fuel_type_by_id(fuel_supply.fuel_type_id) - if fuel_type.unrecognized: - # Unrecognized fuel type, reset specific properties - fuel_supply.ci_of_fuel = None - fuel_supply.energy_density = None - fuel_supply.eer = None - fuel_supply.energy = 0 - else: - # Standard fuel type, set default carbon intensity - fuel_supply.ci_of_fuel = fuel_type.default_carbon_intensity - - # Set the Energy Effectiveness Ratio (EER) based on fuel type, category, and end-use - if ( - fuel_supply.fuel_type_id - and fuel_supply.fuel_category_id - and fuel_supply.end_use_id - ): - energy_effectiveness = await self.fuel_repo.get_energy_effectiveness_ratio( - fuel_supply.fuel_type_id, - fuel_supply.fuel_category_id, - fuel_supply.end_use_id, - ) - fuel_supply.eer = energy_effectiveness.ratio if energy_effectiveness else 1 + # Set calculated fields based on standardized fuel data + fuel_supply.ci_of_fuel = fuel_data["effective_carbon_intensity"] + fuel_supply.target_ci = fuel_data["target_ci"] + fuel_supply.eer = fuel_data["eer"] or 1 # Default EER to 1 if None + fuel_supply.energy_density = ( + fuel_data["energy_density"] or fs_data.energy_density + ) - # Override carbon intensity if a specific fuel code is assigned - if fuel_supply.fuel_code_id: - fuel_code = await self.fuel_repo.get_fuel_code(fuel_supply.fuel_code_id) - fuel_supply.ci_of_fuel = fuel_code.carbon_intensity + # Calculate total energy if energy density is available + fuel_supply.energy = ( + int(fuel_supply.energy_density * fuel_supply.quantity) + if fuel_supply.energy_density + else 0 + ) - # Determine energy density based on fuel type or custom value - if ( - fuel_type.fuel_type - == "Other" # TODO this should be an enum/constant lookup - ): - energy_density = ( - fs_data.energy_density - ) # Use provided energy density for custom types - else: - energy_density = ( - await self.fuel_repo.get_energy_density(fuel_supply.fuel_type_id) - ).density - - fuel_supply.energy_density = energy_density - - # Calculate total energy - if fuel_supply.energy_density: - fuel_supply.energy = int(fuel_supply.energy_density * fuel_supply.quantity) - - # Calculate compliance units for this fuel supply record - fuel_supply.compliance_units = self.calculate_compliance_units_for_supply( - fuel_supply + # Calculate compliance units using the direct utility function + fuel_supply.compliance_units = calculate_compliance_units( + TCI=fuel_supply.target_ci or 0, + EER=fuel_supply.eer or 1, + RCI=fuel_supply.ci_of_fuel or 0, + UCI=0, # Assuming Additional Carbon Intensity Attributable to Use is zero + Q=fuel_supply.quantity or 0, + ED=fuel_supply.energy_density or 0, ) return fuel_supply - async def _populate_and_save_fuel_supply(self, fuel_supply, fs_data): - """ - Helper function to populate fields and save a fuel supply record. - - Uses _populate_fuel_supply_fields to add calculated fields, then saves the populated record. - """ - fuel_supply = await self._populate_fuel_supply_fields(fuel_supply, fs_data) - return await self.repo.create_fuel_supply(fuel_supply) - @service_handler async def create_fuel_supply( self, fs_data: FuelSupplyCreateUpdateSchema, user_type: UserTypeEnum @@ -135,10 +109,16 @@ async def create_fuel_supply( - Assigns a unique group UUID and sets the initial version to 0. - Uses `ActionTypeEnum.CREATE` to indicate a new record. - - Calls `_populate_and_save_fuel_supply` to add calculated fields and save. + - Populates calculated fields and saves the new record. + + Args: + fs_data (FuelSupplyCreateUpdateSchema): The data for the new fuel supply. + user_type (UserTypeEnum): The type of user creating the record. - Returns the newly created fuel supply record as a response schema. + Returns: + FuelSupplyResponseSchema: The newly created fuel supply record as a response schema. """ + # Assign a unique group UUID for the new fuel supply new_group_uuid = str(uuid.uuid4()) fuel_supply = FuelSupply( **fs_data.model_dump(exclude=FUEL_SUPPLY_EXCLUDE_FIELDS), @@ -147,7 +127,12 @@ async def create_fuel_supply( user_type=user_type, action_type=ActionTypeEnum.CREATE, ) - created_supply = await self._populate_and_save_fuel_supply(fuel_supply, fs_data) + + # Populate calculated and referenced fields + fuel_supply = await self._populate_fuel_supply_fields(fuel_supply, fs_data) + + # Save the populated fuel supply record + created_supply = await self.repo.create_fuel_supply(fuel_supply) return FuelSupplyResponseSchema.model_validate(created_supply) @service_handler @@ -159,9 +144,18 @@ async def update_fuel_supply( - Checks if a record exists for the given `group_uuid` and `version`. - If `compliance_report_id` matches, updates the existing record. - - If `compliance_report_id` differs, a new version is created. + - If `compliance_report_id` differs, creates a new version. + - If no existing record is found, raises an HTTPException. + + Args: + fs_data (FuelSupplyCreateUpdateSchema): The data for the fuel supply update. + user_type (UserTypeEnum): The type of user performing the update. - Returns the updated or new version of the fuel supply record. + Returns: + FuelSupplyResponseSchema: The updated or new version of the fuel supply record. + + Raises: + HTTPException: If the fuel supply record is not found. """ existing_fuel_supply = await self.repo.get_fuel_supply_version_by_user( fs_data.group_uuid, fs_data.version, user_type @@ -173,12 +167,17 @@ async def update_fuel_supply( == fs_data.compliance_report_id ): # Update existing record if compliance report ID matches - for field, value in fs_data.model_dump(exclude={"id", "deleted"}).items(): + for field, value in fs_data.model_dump( + exclude=FUEL_SUPPLY_EXCLUDE_FIELDS + ).items(): setattr(existing_fuel_supply, field, value) - updated_supply = await self.repo.update_fuel_supply( - await self._populate_fuel_supply_fields(existing_fuel_supply, fs_data) + # Populate calculated fields + existing_fuel_supply = await self._populate_fuel_supply_fields( + existing_fuel_supply, fs_data ) + + updated_supply = await self.repo.update_fuel_supply(existing_fuel_supply) return FuelSupplyResponseSchema.model_validate(updated_supply) elif existing_fuel_supply: @@ -201,9 +200,14 @@ async def update_fuel_supply( ).items(): setattr(fuel_supply, field, value) - new_supply = await self._populate_and_save_fuel_supply(fuel_supply, fs_data) + # Populate calculated fields + fuel_supply = await self._populate_fuel_supply_fields(fuel_supply, fs_data) + + # Save the new version + new_supply = await self.repo.create_fuel_supply(fuel_supply) return FuelSupplyResponseSchema.model_validate(new_supply) + # Raise an exception if no existing record is found raise HTTPException(status_code=404, detail="Fuel supply record not found.") @service_handler @@ -217,7 +221,12 @@ async def delete_fuel_supply( - If already deleted, returns success immediately. - Otherwise, creates a new version with `ActionTypeEnum.DELETE`. - Returns a response schema confirming deletion. + Args: + fs_data (FuelSupplyCreateUpdateSchema): The data for the fuel supply deletion. + user_type (UserTypeEnum): The type of user performing the deletion. + + Returns: + DeleteFuelSupplyResponseSchema: A response schema confirming deletion. """ existing_fuel_supply = await self.repo.get_latest_fuel_supply_by_group_uuid( fs_data.group_uuid @@ -228,6 +237,7 @@ async def delete_fuel_supply( success=True, message="Already deleted." ) + # Create a new version with action_type DELETE delete_supply = FuelSupply( compliance_report_id=fs_data.compliance_report_id, group_uuid=fs_data.group_uuid, @@ -241,27 +251,10 @@ async def delete_fuel_supply( if field not in FUEL_SUPPLY_EXCLUDE_FIELDS: setattr(delete_supply, field, getattr(existing_fuel_supply, field)) + delete_supply.units = QuantityUnitsEnum(fs_data.units) + + # Save the deletion record await self.repo.create_fuel_supply(delete_supply) return DeleteFuelSupplyResponseSchema( success=True, message="Marked as deleted." ) - - def calculate_compliance_units_for_supply(self, fuel_supply: FuelSupply) -> float: - """ - Calculate compliance units based on fuel supply properties. - - Uses a formula incorporating Target Carbon Intensity (TCI), Recorded Carbon Intensity (RCI), - Energy Effectiveness Ratio (EER), and fuel properties like quantity and energy density. - - Logs the calculation inputs for debugging and returns the computed compliance units. - """ - TCI = fuel_supply.target_ci or 0 - EER = fuel_supply.eer or 0 - RCI = fuel_supply.ci_of_fuel or 0 - Q = fuel_supply.quantity or 0 - ED = fuel_supply.energy_density or 0 - - logger.debug( - f"Calculating compliance units: TCI={TCI}, EER={EER}, RCI={RCI}, Q={Q}, ED={ED}" - ) - return calculate_compliance_units(TCI, EER, RCI, 0, Q, ED) diff --git a/backend/lcfs/web/api/fuel_supply/schema.py b/backend/lcfs/web/api/fuel_supply/schema.py index 1d46fb79..1378bf0c 100644 --- a/backend/lcfs/web/api/fuel_supply/schema.py +++ b/backend/lcfs/web/api/fuel_supply/schema.py @@ -132,6 +132,7 @@ class FuelSupplyCreateUpdateSchema(BaseSchema): fuel_supply_id: Optional[int] = None group_uuid: Optional[str] = None version: Optional[int] = None + compliance_period: Optional[str] = None fuel_type_id: int fuel_category_id: int end_use_id: Optional[int] = None @@ -160,6 +161,7 @@ class FuelSupplyResponseSchema(BaseSchema): action_type: str fuel_type_id: int fuel_type: FuelTypeSchema + compliance_period: Optional[str] = None quantity: int units: str compliance_units: Optional[int] = None diff --git a/backend/lcfs/web/api/fuel_supply/services.py b/backend/lcfs/web/api/fuel_supply/services.py index 38e2169c..d72c9024 100644 --- a/backend/lcfs/web/api/fuel_supply/services.py +++ b/backend/lcfs/web/api/fuel_supply/services.py @@ -233,7 +233,6 @@ async def get_fuel_supply_list( self, compliance_report_id: int ) -> FuelSuppliesSchema: """Get fuel supply list for a compliance report""" - logger.info("Getting fuel supply list for compliance report", compliance_report_id=compliance_report_id) fuel_supply_models = await self.repo.get_fuel_supply_list(compliance_report_id) fs_list = [ FuelSupplyResponseSchema.model_validate(fs) for fs in fuel_supply_models diff --git a/backend/poetry.lock b/backend/poetry.lock index 0a4ea285..902780e6 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "aio-pika" @@ -274,17 +274,17 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "boto3" -version = "1.35.55" +version = "1.35.64" description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" files = [ - {file = "boto3-1.35.55-py3-none-any.whl", hash = "sha256:c7a0a0bc5ae3bed5d38e8bfe5a56b31621e79bdd7c1ea6e5ba4326d820cde3a5"}, - {file = "boto3-1.35.55.tar.gz", hash = "sha256:82fa8cdb00731aeffe7a5829821ae78d75c7ae959b638c15ff3b4681192ace90"}, + {file = "boto3-1.35.64-py3-none-any.whl", hash = "sha256:cdacf03fc750caa3aa0dbf6158166def9922c9d67b4160999ff8fc350662facc"}, + {file = "boto3-1.35.64.tar.gz", hash = "sha256:bc3fc12b41fa2c91e51ab140f74fb1544408a2b1e00f88a4c2369a66d18ddf20"}, ] [package.dependencies] -botocore = ">=1.35.55,<1.36.0" +botocore = ">=1.35.64,<1.36.0" jmespath = ">=0.7.1,<2.0.0" s3transfer = ">=0.10.0,<0.11.0" @@ -293,13 +293,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.35.55" +version = "1.35.64" description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" files = [ - {file = "botocore-1.35.55-py3-none-any.whl", hash = "sha256:3d54739e498534c9d7a6e9732ae2d17ed29c7d5e29fe36c956d8488b859538b0"}, - {file = "botocore-1.35.55.tar.gz", hash = "sha256:61ae18f688250372d7b6046e35c86f8fd09a7c0f0064b52688f3490b4d6c9d6b"}, + {file = "botocore-1.35.64-py3-none-any.whl", hash = "sha256:bbd96bf7f442b1d5e35b36f501076e4a588c83d8d84a1952e9ee1d767e5efb3e"}, + {file = "botocore-1.35.64.tar.gz", hash = "sha256:2f95c83f31c9e38a66995c88810fc638c829790e125032ba00ab081a2cf48cb9"}, ] [package.dependencies] @@ -458,73 +458,73 @@ files = [ [[package]] name = "coverage" -version = "7.6.4" +version = "7.6.7" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f8ae553cba74085db385d489c7a792ad66f7f9ba2ee85bfa508aeb84cf0ba07"}, - {file = "coverage-7.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8165b796df0bd42e10527a3f493c592ba494f16ef3c8b531288e3d0d72c1f6f0"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7c8b95bf47db6d19096a5e052ffca0a05f335bc63cef281a6e8fe864d450a72"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ed9281d1b52628e81393f5eaee24a45cbd64965f41857559c2b7ff19385df51"}, - {file = "coverage-7.6.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0809082ee480bb8f7416507538243c8863ac74fd8a5d2485c46f0f7499f2b491"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d541423cdd416b78626b55f123412fcf979d22a2c39fce251b350de38c15c15b"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58809e238a8a12a625c70450b48e8767cff9eb67c62e6154a642b21ddf79baea"}, - {file = "coverage-7.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c9b8e184898ed014884ca84c70562b4a82cbc63b044d366fedc68bc2b2f3394a"}, - {file = "coverage-7.6.4-cp310-cp310-win32.whl", hash = "sha256:6bd818b7ea14bc6e1f06e241e8234508b21edf1b242d49831831a9450e2f35fa"}, - {file = "coverage-7.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:06babbb8f4e74b063dbaeb74ad68dfce9186c595a15f11f5d5683f748fa1d172"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73d2b73584446e66ee633eaad1a56aad577c077f46c35ca3283cd687b7715b0b"}, - {file = "coverage-7.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:51b44306032045b383a7a8a2c13878de375117946d68dcb54308111f39775a25"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3fb02fe73bed561fa12d279a417b432e5b50fe03e8d663d61b3d5990f29546"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed8fe9189d2beb6edc14d3ad19800626e1d9f2d975e436f84e19efb7fa19469b"}, - {file = "coverage-7.6.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b369ead6527d025a0fe7bd3864e46dbee3aa8f652d48df6174f8d0bac9e26e0e"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ade3ca1e5f0ff46b678b66201f7ff477e8fa11fb537f3b55c3f0568fbfe6e718"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:27fb4a050aaf18772db513091c9c13f6cb94ed40eacdef8dad8411d92d9992db"}, - {file = "coverage-7.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4f704f0998911abf728a7783799444fcbbe8261c4a6c166f667937ae6a8aa522"}, - {file = "coverage-7.6.4-cp311-cp311-win32.whl", hash = "sha256:29155cd511ee058e260db648b6182c419422a0d2e9a4fa44501898cf918866cf"}, - {file = "coverage-7.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:8902dd6a30173d4ef09954bfcb24b5d7b5190cf14a43170e386979651e09ba19"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12394842a3a8affa3ba62b0d4ab7e9e210c5e366fbac3e8b2a68636fb19892c2"}, - {file = "coverage-7.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b6b4c83d8e8ea79f27ab80778c19bc037759aea298da4b56621f4474ffeb117"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d5b8007f81b88696d06f7df0cb9af0d3b835fe0c8dbf489bad70b45f0e45613"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b57b768feb866f44eeed9f46975f3d6406380275c5ddfe22f531a2bf187eda27"}, - {file = "coverage-7.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5915fcdec0e54ee229926868e9b08586376cae1f5faa9bbaf8faf3561b393d52"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0b58c672d14f16ed92a48db984612f5ce3836ae7d72cdd161001cc54512571f2"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2fdef0d83a2d08d69b1f2210a93c416d54e14d9eb398f6ab2f0a209433db19e1"}, - {file = "coverage-7.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8cf717ee42012be8c0cb205dbbf18ffa9003c4cbf4ad078db47b95e10748eec5"}, - {file = "coverage-7.6.4-cp312-cp312-win32.whl", hash = "sha256:7bb92c539a624cf86296dd0c68cd5cc286c9eef2d0c3b8b192b604ce9de20a17"}, - {file = "coverage-7.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:1032e178b76a4e2b5b32e19d0fd0abbce4b58e77a1ca695820d10e491fa32b08"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:023bf8ee3ec6d35af9c1c6ccc1d18fa69afa1cb29eaac57cb064dbb262a517f9"}, - {file = "coverage-7.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b0ac3d42cb51c4b12df9c5f0dd2f13a4f24f01943627120ec4d293c9181219ba"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8fe4984b431f8621ca53d9380901f62bfb54ff759a1348cd140490ada7b693c"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5fbd612f8a091954a0c8dd4c0b571b973487277d26476f8480bfa4b2a65b5d06"}, - {file = "coverage-7.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dacbc52de979f2823a819571f2e3a350a7e36b8cb7484cdb1e289bceaf35305f"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dab4d16dfef34b185032580e2f2f89253d302facba093d5fa9dbe04f569c4f4b"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:862264b12ebb65ad8d863d51f17758b1684560b66ab02770d4f0baf2ff75da21"}, - {file = "coverage-7.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5beb1ee382ad32afe424097de57134175fea3faf847b9af002cc7895be4e2a5a"}, - {file = "coverage-7.6.4-cp313-cp313-win32.whl", hash = "sha256:bf20494da9653f6410213424f5f8ad0ed885e01f7e8e59811f572bdb20b8972e"}, - {file = "coverage-7.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:182e6cd5c040cec0a1c8d415a87b67ed01193ed9ad458ee427741c7d8513d963"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a181e99301a0ae128493a24cfe5cfb5b488c4e0bf2f8702091473d033494d04f"}, - {file = "coverage-7.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:df57bdbeffe694e7842092c5e2e0bc80fff7f43379d465f932ef36f027179806"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcd1069e710600e8e4cf27f65c90c7843fa8edfb4520fb0ccb88894cad08b11"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99b41d18e6b2a48ba949418db48159d7a2e81c5cc290fc934b7d2380515bd0e3"}, - {file = "coverage-7.6.4-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6b1e54712ba3474f34b7ef7a41e65bd9037ad47916ccb1cc78769bae324c01a"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:53d202fd109416ce011578f321460795abfe10bb901b883cafd9b3ef851bacfc"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:c48167910a8f644671de9f2083a23630fbf7a1cb70ce939440cd3328e0919f70"}, - {file = "coverage-7.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cc8ff50b50ce532de2fa7a7daae9dd12f0a699bfcd47f20945364e5c31799fef"}, - {file = "coverage-7.6.4-cp313-cp313t-win32.whl", hash = "sha256:b8d3a03d9bfcaf5b0141d07a88456bb6a4c3ce55c080712fec8418ef3610230e"}, - {file = "coverage-7.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:f3ddf056d3ebcf6ce47bdaf56142af51bb7fad09e4af310241e9db7a3a8022e1"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9cb7fa111d21a6b55cbf633039f7bc2749e74932e3aa7cb7333f675a58a58bf3"}, - {file = "coverage-7.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:11a223a14e91a4693d2d0755c7a043db43d96a7450b4f356d506c2562c48642c"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a413a096c4cbac202433c850ee43fa326d2e871b24554da8327b01632673a076"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00a1d69c112ff5149cabe60d2e2ee948752c975d95f1e1096742e6077affd376"}, - {file = "coverage-7.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f76846299ba5c54d12c91d776d9605ae33f8ae2b9d1d3c3703cf2db1a67f2c0"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:fe439416eb6380de434886b00c859304338f8b19f6f54811984f3420a2e03858"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0294ca37f1ba500667b1aef631e48d875ced93ad5e06fa665a3295bdd1d95111"}, - {file = "coverage-7.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6f01ba56b1c0e9d149f9ac85a2f999724895229eb36bd997b61e62999e9b0901"}, - {file = "coverage-7.6.4-cp39-cp39-win32.whl", hash = "sha256:bc66f0bf1d7730a17430a50163bb264ba9ded56739112368ba985ddaa9c3bd09"}, - {file = "coverage-7.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:c481b47f6b5845064c65a7bc78bc0860e635a9b055af0df46fdf1c58cebf8e8f"}, - {file = "coverage-7.6.4-pp39.pp310-none-any.whl", hash = "sha256:3c65d37f3a9ebb703e710befdc489a38683a5b152242664b973a7b7b22348a4e"}, - {file = "coverage-7.6.4.tar.gz", hash = "sha256:29fc0f17b1d3fea332f8001d4558f8214af7f1d87a345f3a133c901d60347c73"}, + {file = "coverage-7.6.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:108bb458827765d538abcbf8288599fee07d2743357bdd9b9dad456c287e121e"}, + {file = "coverage-7.6.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c973b2fe4dc445cb865ab369df7521df9c27bf40715c837a113edaa2aa9faf45"}, + {file = "coverage-7.6.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c6b24007c4bcd0b19fac25763a7cac5035c735ae017e9a349b927cfc88f31c1"}, + {file = "coverage-7.6.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:acbb8af78f8f91b3b51f58f288c0994ba63c646bc1a8a22ad072e4e7e0a49f1c"}, + {file = "coverage-7.6.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad32a981bcdedb8d2ace03b05e4fd8dace8901eec64a532b00b15217d3677dd2"}, + {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:34d23e28ccb26236718a3a78ba72744212aa383141961dd6825f6595005c8b06"}, + {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e25bacb53a8c7325e34d45dddd2f2fbae0dbc230d0e2642e264a64e17322a777"}, + {file = "coverage-7.6.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:af05bbba896c4472a29408455fe31b3797b4d8648ed0a2ccac03e074a77e2314"}, + {file = "coverage-7.6.7-cp310-cp310-win32.whl", hash = "sha256:796c9b107d11d2d69e1849b2dfe41730134b526a49d3acb98ca02f4985eeff7a"}, + {file = "coverage-7.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:987a8e3da7da4eed10a20491cf790589a8e5e07656b6dc22d3814c4d88faf163"}, + {file = "coverage-7.6.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7e61b0e77ff4dddebb35a0e8bb5a68bf0f8b872407d8d9f0c726b65dfabe2469"}, + {file = "coverage-7.6.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a5407a75ca4abc20d6252efeb238377a71ce7bda849c26c7a9bece8680a5d99"}, + {file = "coverage-7.6.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df002e59f2d29e889c37abd0b9ee0d0e6e38c24f5f55d71ff0e09e3412a340ec"}, + {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:673184b3156cba06154825f25af33baa2671ddae6343f23175764e65a8c4c30b"}, + {file = "coverage-7.6.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e69ad502f1a2243f739f5bd60565d14a278be58be4c137d90799f2c263e7049a"}, + {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60dcf7605c50ea72a14490d0756daffef77a5be15ed1b9fea468b1c7bda1bc3b"}, + {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9c2eb378bebb2c8f65befcb5147877fc1c9fbc640fc0aad3add759b5df79d55d"}, + {file = "coverage-7.6.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3c0317288f032221d35fa4cbc35d9f4923ff0dfd176c79c9b356e8ef8ef2dff4"}, + {file = "coverage-7.6.7-cp311-cp311-win32.whl", hash = "sha256:951aade8297358f3618a6e0660dc74f6b52233c42089d28525749fc8267dccd2"}, + {file = "coverage-7.6.7-cp311-cp311-win_amd64.whl", hash = "sha256:5e444b8e88339a2a67ce07d41faabb1d60d1004820cee5a2c2b54e2d8e429a0f"}, + {file = "coverage-7.6.7-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f07ff574986bc3edb80e2c36391678a271d555f91fd1d332a1e0f4b5ea4b6ea9"}, + {file = "coverage-7.6.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:49ed5ee4109258973630c1f9d099c7e72c5c36605029f3a91fe9982c6076c82b"}, + {file = "coverage-7.6.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3e8796434a8106b3ac025fd15417315d7a58ee3e600ad4dbcfddc3f4b14342c"}, + {file = "coverage-7.6.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3b925300484a3294d1c70f6b2b810d6526f2929de954e5b6be2bf8caa1f12c1"}, + {file = "coverage-7.6.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c42ec2c522e3ddd683dec5cdce8e62817afb648caedad9da725001fa530d354"}, + {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0266b62cbea568bd5e93a4da364d05de422110cbed5056d69339bd5af5685433"}, + {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e5f2a0f161d126ccc7038f1f3029184dbdf8f018230af17ef6fd6a707a5b881f"}, + {file = "coverage-7.6.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c132b5a22821f9b143f87446805e13580b67c670a548b96da945a8f6b4f2efbb"}, + {file = "coverage-7.6.7-cp312-cp312-win32.whl", hash = "sha256:7c07de0d2a110f02af30883cd7dddbe704887617d5c27cf373362667445a4c76"}, + {file = "coverage-7.6.7-cp312-cp312-win_amd64.whl", hash = "sha256:fd49c01e5057a451c30c9b892948976f5d38f2cbd04dc556a82743ba8e27ed8c"}, + {file = "coverage-7.6.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:46f21663e358beae6b368429ffadf14ed0a329996248a847a4322fb2e35d64d3"}, + {file = "coverage-7.6.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:40cca284c7c310d622a1677f105e8507441d1bb7c226f41978ba7c86979609ab"}, + {file = "coverage-7.6.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77256ad2345c29fe59ae861aa11cfc74579c88d4e8dbf121cbe46b8e32aec808"}, + {file = "coverage-7.6.7-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87ea64b9fa52bf395272e54020537990a28078478167ade6c61da7ac04dc14bc"}, + {file = "coverage-7.6.7-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d608a7808793e3615e54e9267519351c3ae204a6d85764d8337bd95993581a8"}, + {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdd94501d65adc5c24f8a1a0eda110452ba62b3f4aeaba01e021c1ed9cb8f34a"}, + {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82c809a62e953867cf57e0548c2b8464207f5f3a6ff0e1e961683e79b89f2c55"}, + {file = "coverage-7.6.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:bb684694e99d0b791a43e9fc0fa58efc15ec357ac48d25b619f207c41f2fd384"}, + {file = "coverage-7.6.7-cp313-cp313-win32.whl", hash = "sha256:963e4a08cbb0af6623e61492c0ec4c0ec5c5cf74db5f6564f98248d27ee57d30"}, + {file = "coverage-7.6.7-cp313-cp313-win_amd64.whl", hash = "sha256:14045b8bfd5909196a90da145a37f9d335a5d988a83db34e80f41e965fb7cb42"}, + {file = "coverage-7.6.7-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:f2c7a045eef561e9544359a0bf5784b44e55cefc7261a20e730baa9220c83413"}, + {file = "coverage-7.6.7-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dd4e4a49d9c72a38d18d641135d2fb0bdf7b726ca60a103836b3d00a1182acd"}, + {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c95e0fa3d1547cb6f021ab72f5c23402da2358beec0a8e6d19a368bd7b0fb37"}, + {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f63e21ed474edd23f7501f89b53280014436e383a14b9bd77a648366c81dce7b"}, + {file = "coverage-7.6.7-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ead9b9605c54d15be228687552916c89c9683c215370c4a44f1f217d2adcc34d"}, + {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:0573f5cbf39114270842d01872952d301027d2d6e2d84013f30966313cadb529"}, + {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:e2c8e3384c12dfa19fa9a52f23eb091a8fad93b5b81a41b14c17c78e23dd1d8b"}, + {file = "coverage-7.6.7-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:70a56a2ec1869e6e9fa69ef6b76b1a8a7ef709972b9cc473f9ce9d26b5997ce3"}, + {file = "coverage-7.6.7-cp313-cp313t-win32.whl", hash = "sha256:dbba8210f5067398b2c4d96b4e64d8fb943644d5eb70be0d989067c8ca40c0f8"}, + {file = "coverage-7.6.7-cp313-cp313t-win_amd64.whl", hash = "sha256:dfd14bcae0c94004baba5184d1c935ae0d1231b8409eb6c103a5fd75e8ecdc56"}, + {file = "coverage-7.6.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37a15573f988b67f7348916077c6d8ad43adb75e478d0910957394df397d2874"}, + {file = "coverage-7.6.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b6cce5c76985f81da3769c52203ee94722cd5d5889731cd70d31fee939b74bf0"}, + {file = "coverage-7.6.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ab9763d291a17b527ac6fd11d1a9a9c358280adb320e9c2672a97af346ac2c"}, + {file = "coverage-7.6.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cf96ceaa275f071f1bea3067f8fd43bec184a25a962c754024c973af871e1b7"}, + {file = "coverage-7.6.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aee9cf6b0134d6f932d219ce253ef0e624f4fa588ee64830fcba193269e4daa3"}, + {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2bc3e45c16564cc72de09e37413262b9f99167803e5e48c6156bccdfb22c8327"}, + {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:623e6965dcf4e28a3debaa6fcf4b99ee06d27218f46d43befe4db1c70841551c"}, + {file = "coverage-7.6.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:850cfd2d6fc26f8346f422920ac204e1d28814e32e3a58c19c91980fa74d8289"}, + {file = "coverage-7.6.7-cp39-cp39-win32.whl", hash = "sha256:c296263093f099da4f51b3dff1eff5d4959b527d4f2f419e16508c5da9e15e8c"}, + {file = "coverage-7.6.7-cp39-cp39-win_amd64.whl", hash = "sha256:90746521206c88bdb305a4bf3342b1b7316ab80f804d40c536fc7d329301ee13"}, + {file = "coverage-7.6.7-pp39.pp310-none-any.whl", hash = "sha256:0ddcb70b3a3a57581b450571b31cb774f23eb9519c2aaa6176d3a84c9fc57671"}, + {file = "coverage-7.6.7.tar.gz", hash = "sha256:d79d4826e41441c9a118ff045e4bccb9fdbdcb1d02413e7ea6eb5c87b5439d24"}, ] [package.dependencies] @@ -595,37 +595,37 @@ files = [ [[package]] name = "debugpy" -version = "1.8.7" +version = "1.8.8" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.7-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:95fe04a573b8b22896c404365e03f4eda0ce0ba135b7667a1e57bd079793b96b"}, - {file = "debugpy-1.8.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:628a11f4b295ffb4141d8242a9bb52b77ad4a63a2ad19217a93be0f77f2c28c9"}, - {file = "debugpy-1.8.7-cp310-cp310-win32.whl", hash = "sha256:85ce9c1d0eebf622f86cc68618ad64bf66c4fc3197d88f74bb695a416837dd55"}, - {file = "debugpy-1.8.7-cp310-cp310-win_amd64.whl", hash = "sha256:29e1571c276d643757ea126d014abda081eb5ea4c851628b33de0c2b6245b037"}, - {file = "debugpy-1.8.7-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:caf528ff9e7308b74a1749c183d6808ffbedbb9fb6af78b033c28974d9b8831f"}, - {file = "debugpy-1.8.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cba1d078cf2e1e0b8402e6bda528bf8fda7ccd158c3dba6c012b7897747c41a0"}, - {file = "debugpy-1.8.7-cp311-cp311-win32.whl", hash = "sha256:171899588bcd412151e593bd40d9907133a7622cd6ecdbdb75f89d1551df13c2"}, - {file = "debugpy-1.8.7-cp311-cp311-win_amd64.whl", hash = "sha256:6e1c4ffb0c79f66e89dfd97944f335880f0d50ad29525dc792785384923e2211"}, - {file = "debugpy-1.8.7-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:4d27d842311353ede0ad572600c62e4bcd74f458ee01ab0dd3a1a4457e7e3706"}, - {file = "debugpy-1.8.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:703c1fd62ae0356e194f3e7b7a92acd931f71fe81c4b3be2c17a7b8a4b546ec2"}, - {file = "debugpy-1.8.7-cp312-cp312-win32.whl", hash = "sha256:2f729228430ef191c1e4df72a75ac94e9bf77413ce5f3f900018712c9da0aaca"}, - {file = "debugpy-1.8.7-cp312-cp312-win_amd64.whl", hash = "sha256:45c30aaefb3e1975e8a0258f5bbd26cd40cde9bfe71e9e5a7ac82e79bad64e39"}, - {file = "debugpy-1.8.7-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:d050a1ec7e925f514f0f6594a1e522580317da31fbda1af71d1530d6ea1f2b40"}, - {file = "debugpy-1.8.7-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f4349a28e3228a42958f8ddaa6333d6f8282d5edaea456070e48609c5983b7"}, - {file = "debugpy-1.8.7-cp313-cp313-win32.whl", hash = "sha256:11ad72eb9ddb436afb8337891a986302e14944f0f755fd94e90d0d71e9100bba"}, - {file = "debugpy-1.8.7-cp313-cp313-win_amd64.whl", hash = "sha256:2efb84d6789352d7950b03d7f866e6d180284bc02c7e12cb37b489b7083d81aa"}, - {file = "debugpy-1.8.7-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:4b908291a1d051ef3331484de8e959ef3e66f12b5e610c203b5b75d2725613a7"}, - {file = "debugpy-1.8.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da8df5b89a41f1fd31503b179d0a84a5fdb752dddd5b5388dbd1ae23cda31ce9"}, - {file = "debugpy-1.8.7-cp38-cp38-win32.whl", hash = "sha256:b12515e04720e9e5c2216cc7086d0edadf25d7ab7e3564ec8b4521cf111b4f8c"}, - {file = "debugpy-1.8.7-cp38-cp38-win_amd64.whl", hash = "sha256:93176e7672551cb5281577cdb62c63aadc87ec036f0c6a486f0ded337c504596"}, - {file = "debugpy-1.8.7-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:90d93e4f2db442f8222dec5ec55ccfc8005821028982f1968ebf551d32b28907"}, - {file = "debugpy-1.8.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6db2a370e2700557a976eaadb16243ec9c91bd46f1b3bb15376d7aaa7632c81"}, - {file = "debugpy-1.8.7-cp39-cp39-win32.whl", hash = "sha256:a6cf2510740e0c0b4a40330640e4b454f928c7b99b0c9dbf48b11efba08a8cda"}, - {file = "debugpy-1.8.7-cp39-cp39-win_amd64.whl", hash = "sha256:6a9d9d6d31846d8e34f52987ee0f1a904c7baa4912bf4843ab39dadf9b8f3e0d"}, - {file = "debugpy-1.8.7-py2.py3-none-any.whl", hash = "sha256:57b00de1c8d2c84a61b90880f7e5b6deaf4c312ecbde3a0e8912f2a56c4ac9ae"}, - {file = "debugpy-1.8.7.zip", hash = "sha256:18b8f731ed3e2e1df8e9cdaa23fb1fc9c24e570cd0081625308ec51c82efe42e"}, + {file = "debugpy-1.8.8-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:e59b1607c51b71545cb3496876544f7186a7a27c00b436a62f285603cc68d1c6"}, + {file = "debugpy-1.8.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6531d952b565b7cb2fbd1ef5df3d333cf160b44f37547a4e7cf73666aca5d8d"}, + {file = "debugpy-1.8.8-cp310-cp310-win32.whl", hash = "sha256:b01f4a5e5c5fb1d34f4ccba99a20ed01eabc45a4684f4948b5db17a319dfb23f"}, + {file = "debugpy-1.8.8-cp310-cp310-win_amd64.whl", hash = "sha256:535f4fb1c024ddca5913bb0eb17880c8f24ba28aa2c225059db145ee557035e9"}, + {file = "debugpy-1.8.8-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:c399023146e40ae373753a58d1be0a98bf6397fadc737b97ad612886b53df318"}, + {file = "debugpy-1.8.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09cc7b162586ea2171eea055985da2702b0723f6f907a423c9b2da5996ad67ba"}, + {file = "debugpy-1.8.8-cp311-cp311-win32.whl", hash = "sha256:eea8821d998ebeb02f0625dd0d76839ddde8cbf8152ebbe289dd7acf2cdc6b98"}, + {file = "debugpy-1.8.8-cp311-cp311-win_amd64.whl", hash = "sha256:d4483836da2a533f4b1454dffc9f668096ac0433de855f0c22cdce8c9f7e10c4"}, + {file = "debugpy-1.8.8-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:0cc94186340be87b9ac5a707184ec8f36547fb66636d1029ff4f1cc020e53996"}, + {file = "debugpy-1.8.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64674e95916e53c2e9540a056e5f489e0ad4872645399d778f7c598eacb7b7f9"}, + {file = "debugpy-1.8.8-cp312-cp312-win32.whl", hash = "sha256:5c6e885dbf12015aed73770f29dec7023cb310d0dc2ba8bfbeb5c8e43f80edc9"}, + {file = "debugpy-1.8.8-cp312-cp312-win_amd64.whl", hash = "sha256:19ffbd84e757a6ca0113574d1bf5a2298b3947320a3e9d7d8dc3377f02d9f864"}, + {file = "debugpy-1.8.8-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:705cd123a773d184860ed8dae99becd879dfec361098edbefb5fc0d3683eb804"}, + {file = "debugpy-1.8.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890fd16803f50aa9cb1a9b9b25b5ec321656dd6b78157c74283de241993d086f"}, + {file = "debugpy-1.8.8-cp313-cp313-win32.whl", hash = "sha256:90244598214bbe704aa47556ec591d2f9869ff9e042e301a2859c57106649add"}, + {file = "debugpy-1.8.8-cp313-cp313-win_amd64.whl", hash = "sha256:4b93e4832fd4a759a0c465c967214ed0c8a6e8914bced63a28ddb0dd8c5f078b"}, + {file = "debugpy-1.8.8-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:143ef07940aeb8e7316de48f5ed9447644da5203726fca378f3a6952a50a9eae"}, + {file = "debugpy-1.8.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f95651bdcbfd3b27a408869a53fbefcc2bcae13b694daee5f1365b1b83a00113"}, + {file = "debugpy-1.8.8-cp38-cp38-win32.whl", hash = "sha256:26b461123a030e82602a750fb24d7801776aa81cd78404e54ab60e8b5fecdad5"}, + {file = "debugpy-1.8.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3cbf1833e644a3100eadb6120f25be8a532035e8245584c4f7532937edc652a"}, + {file = "debugpy-1.8.8-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:53709d4ec586b525724819dc6af1a7703502f7e06f34ded7157f7b1f963bb854"}, + {file = "debugpy-1.8.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a9c013077a3a0000e83d97cf9cc9328d2b0bbb31f56b0e99ea3662d29d7a6a2"}, + {file = "debugpy-1.8.8-cp39-cp39-win32.whl", hash = "sha256:ffe94dd5e9a6739a75f0b85316dc185560db3e97afa6b215628d1b6a17561cb2"}, + {file = "debugpy-1.8.8-cp39-cp39-win_amd64.whl", hash = "sha256:5c0e5a38c7f9b481bf31277d2f74d2109292179081f11108e668195ef926c0f9"}, + {file = "debugpy-1.8.8-py2.py3-none-any.whl", hash = "sha256:ec684553aba5b4066d4de510859922419febc710df7bba04fe9e7ef3de15d34f"}, + {file = "debugpy-1.8.8.zip", hash = "sha256:e6355385db85cbd666be703a96ab7351bc9e6c61d694893206f8001e22aee091"}, ] [[package]] @@ -746,13 +746,13 @@ probabilistic = ["pyprobables (>=0.6,<0.7)"] [[package]] name = "fastapi" -version = "0.115.4" +version = "0.115.5" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.115.4-py3-none-any.whl", hash = "sha256:0b504a063ffb3cf96a5e27dc1bc32c80ca743a2528574f9cdc77daa2d31b4742"}, - {file = "fastapi-0.115.4.tar.gz", hash = "sha256:db653475586b091cb8b2fec2ac54a680ac6a158e07406e1abae31679e8826349"}, + {file = "fastapi-0.115.5-py3-none-any.whl", hash = "sha256:596b95adbe1474da47049e802f9a65ab2ffa9c2b07e7efee70eb8a66c9f2f796"}, + {file = "fastapi-0.115.5.tar.gz", hash = "sha256:0e7a4d0dc0d01c68df21887cce0945e72d3c48b9f4f79dfe7a7d53aa08fbb289"}, ] [package.dependencies] @@ -1317,13 +1317,13 @@ socks = ["socksio (==1.*)"] [[package]] name = "identify" -version = "2.6.1" +version = "2.6.2" description = "File identification library for Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, - {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, + {file = "identify-2.6.2-py2.py3-none-any.whl", hash = "sha256:c097384259f49e372f4ea00a19719d95ae27dd5ff0fd77ad630aa891306b82f3"}, + {file = "identify-2.6.2.tar.gz", hash = "sha256:fab5c716c24d7a789775228823797296a2994b075fb6080ac83a102772a98cbd"}, ] [package.extras] @@ -1381,42 +1381,52 @@ files = [ [[package]] name = "libcst" -version = "1.5.0" +version = "1.5.1" description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.13 programs." optional = false python-versions = ">=3.9" files = [ - {file = "libcst-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:23d0e07fd3ed11480f8993a1e99d58a45f914a711b14f858b8db08ae861a8a34"}, - {file = "libcst-1.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d92c5ae2e2dc9356ad7e3d05077d9b7e5065423e45788fd86729c88729e45c6e"}, - {file = "libcst-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96adc45e96476350df6b8a5ddbb1e1d6a83a7eb3f13087e52eb7cd2f9b65bcc7"}, - {file = "libcst-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5978fd60c66794bb60d037b2e6427ea52d032636e84afce32b0f04e1cf500a"}, - {file = "libcst-1.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6502aeb11412afc759036160c686be1107eb5a4466db56b207c786b9b4da7c4"}, - {file = "libcst-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9cccfc0a78e110c0d0a9d2c6fdeb29feb5274c9157508a8baef7edf352420f6d"}, - {file = "libcst-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:585b3aa705b3767d717d2100935d8ef557275ecdd3fac81c3e28db0959efb0ea"}, - {file = "libcst-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8935dd3393e30c2f97344866a4cb14efe560200e232166a8db1de7865c2ef8b2"}, - {file = "libcst-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc80ea16c7d44e38f193e4d4ef7ff1e0ba72d8e60e8b61ac6f4c87f070a118bd"}, - {file = "libcst-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02be4aab728261bb76d16e77c9a457884cebb60d09c8edee844de43b0e08aff7"}, - {file = "libcst-1.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8fcd78be4d9ce3c36d0c5d0bdd384e0c7d5f72970a9e4ebd56070141972b4ad"}, - {file = "libcst-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:52b6aadfe54e3ae52c3b815eaaa17ba4da9ff010d5e8adf6a70697872886dd10"}, - {file = "libcst-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:83bc5fbe34d33597af1d5ea113dcb9b5dd5afe5a5f4316bac4293464d5e3971a"}, - {file = "libcst-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f10124bf99a0b075eae136ef0ce06204e5f6b8da4596a9c4853a0663e80ddf3"}, - {file = "libcst-1.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48e581af6127c5af4c9f483e5986d94f0c6b2366967ee134f0a8eba0aa4c8c12"}, - {file = "libcst-1.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dba93cca0a5c6d771ed444c44d21ce8ea9b277af7036cea3743677aba9fbbb8"}, - {file = "libcst-1.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b5c4d87721a7bab265c202575809b810815ab81d5e2e7a5d4417a087975840"}, - {file = "libcst-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:b48bf71d52c1e891a0948465a94d9817b5fc1ec1a09603566af90585f3b11948"}, - {file = "libcst-1.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:88520b6dea59eaea0cae80f77c0a632604a82c5b2d23dedb4b5b34035cbf1615"}, - {file = "libcst-1.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:208ea92d80b2eeed8cbc879d5f39f241582a5d56b916b1b65ed2be2f878a2425"}, - {file = "libcst-1.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4592872aaf5b7fa5c2727a7d73c0985261f1b3fe7eff51f4fd5b8174f30b4e2"}, - {file = "libcst-1.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2788b2b5838b78fe15df8e9fa6b6903195ea49b2d2ba43e8f423f6c90e4b69f"}, - {file = "libcst-1.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b5b5bcd3a9ba92840f27ad34eaa038acbee195ec337da39536c0a2efbbf28efd"}, - {file = "libcst-1.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:4d6acb0bdee1e55b44c6215c59755ec4693ac01e74bb1fde04c37358b378835d"}, - {file = "libcst-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6453b5a8755a6eee3ad67ee246f13a8eac9827d2cfc8e4a269e8bf0393db74bc"}, - {file = "libcst-1.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:40748361f4ea66ab6cdd82f8501c82c29808317ac7a3bd132074efd5fd9bfae2"}, - {file = "libcst-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f71aed85932c2ea92058fd9bbd99a6478bd69eada041c3726b4f4c9af1f564e"}, - {file = "libcst-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b60b09abcc2848ab52d479c3a9b71b606d91a941e3779616efd083bb87dbe8ad"}, - {file = "libcst-1.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fb324ed20f3a725d152df5dba8d80f7e126d9c93cced581bf118a5fc18c1065"}, - {file = "libcst-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:99e7c52150a135d66716b03e00c7b1859a44336dc2a2bf8f9acc164494308531"}, - {file = "libcst-1.5.0.tar.gz", hash = "sha256:8478abf21ae3861a073e898d80b822bd56e578886331b33129ba77fec05b8c24"}, + {file = "libcst-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ab83633e61ee91df575a3838b1e73c371f19d4916bf1816554933235553d41ea"}, + {file = "libcst-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b58a49895d95ec1fd34fad041a142d98edf9b51fcaf632337c13befeb4d51c7c"}, + {file = "libcst-1.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d9ec764aa781ef35ab96b693569ac3dced16df9feb40ee6c274d13e86a1472e"}, + {file = "libcst-1.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99bbffd8596d192bc0e844a4cf3c4fc696979d4e20ab1c0774a01768a59b47ed"}, + {file = "libcst-1.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec6ee607cfe4cc4cc93e56e0188fdb9e50399d61a1262d58229752946f288f5e"}, + {file = "libcst-1.5.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:72132756f985a19ef64d702a821099d4afc3544974662772b44cbc55b7279727"}, + {file = "libcst-1.5.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:40b75bf2d70fc0bc26b1fa73e61bdc46fef59f5c71aedf16128e7c33db8d5e40"}, + {file = "libcst-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:56c944acaa781b8e586df3019374f5cf117054d7fc98f85be1ba84fe810005dc"}, + {file = "libcst-1.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:db7711a762b0327b581be5a963908fecd74412bdda34db34553faa521563c22d"}, + {file = "libcst-1.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:aa524bd012aaae1f485fd44490ef5abf708b14d2addc0f06b28de3e4585c4b9e"}, + {file = "libcst-1.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ffb8135c09e41e8cf710b152c33e9b7f1d0d0b9f242bae0c502eb082fdb1fb"}, + {file = "libcst-1.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76a8ac7a84f9b6f678a668bff85b360e0a93fa8d7f25a74a206a28110734bb2a"}, + {file = "libcst-1.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89c808bdb5fa9ca02df41dd234cbb0e9de0d2e0c029c7063d5435a9f6781cc10"}, + {file = "libcst-1.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40fbbaa8b839bfbfa5b300623ca2b6b0768b58bbc31b341afbc99110c9bee232"}, + {file = "libcst-1.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c7021e3904d8d088c369afc3fe17c279883e583415ef07edacadba76cfbecd27"}, + {file = "libcst-1.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:f053a5deb6a214972dbe9fa26ecd8255edb903de084a3d7715bf9e9da8821c50"}, + {file = "libcst-1.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:666813950b8637af0c0e96b1ca46f5d5f183d2fe50bbac2186f5b283a99f3529"}, + {file = "libcst-1.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b7b58b36022ae77a5a00002854043ae95c03e92f6062ad08473eff326f32efa0"}, + {file = "libcst-1.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb13d7c598fe9a798a1d22eae56ab3d3d599b38b83436039bd6ae229fc854d7"}, + {file = "libcst-1.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5987daff8389b0df60b5c20499ff4fb73fc03cb3ae1f6a746eefd204ed08df85"}, + {file = "libcst-1.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00f3d2f32ee081bad3394546b0b9ac5e31686d3b5cfe4892d716d2ba65f9ec08"}, + {file = "libcst-1.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ff21005c33b634957a98db438e882522febf1cacc62fa716f29e163a3f5871a"}, + {file = "libcst-1.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:15697ea9f1edbb9a263364d966c72abda07195d1c1a6838eb79af057f1040770"}, + {file = "libcst-1.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:cedd4c8336e01c51913113fbf5566b8f61a86d90f3d5cc5b1cb5049575622c5f"}, + {file = "libcst-1.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:06a9b4c9b76da4a7399e6f1f3a325196fb5febd3ea59fac1f68e2116f3517cd8"}, + {file = "libcst-1.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:940ec4c8db4c2d620a7268d6c83e64ff646e4afd74ae5183d0f0ef3b80e05be0"}, + {file = "libcst-1.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fbccb016b1ac6d892344300dcccc8a16887b71bb7f875ba56c0ed6c1a7ade8be"}, + {file = "libcst-1.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c615af2117320e9a218083c83ec61227d3547e38a0de80329376971765f27a9e"}, + {file = "libcst-1.5.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:02b38fa4d9f13e79fe69e9b5407b9e173557bcfb5960f7866cf4145af9c7ae09"}, + {file = "libcst-1.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3334afe9e7270e175de01198f816b0dc78dda94d9d72152b61851c323e4e741e"}, + {file = "libcst-1.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:26c804fa8091747128579013df0b5f8e6b0c7904d9c4ee83841f136f53e18684"}, + {file = "libcst-1.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:b5a0d3c632aa2b21c5fa145e4e8dbf86f45c9b37a64c0b7221a5a45caf58915a"}, + {file = "libcst-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1cc7393aaac733e963f0ee00466d059db74a38e15fc7e6a46dddd128c5be8d08"}, + {file = "libcst-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bbaf5755be50fa9b35a3d553d1e62293fbb2ee5ce2c16c7e7ffeb2746af1ab88"}, + {file = "libcst-1.5.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e397f5b6c0fc271acea44579f154b0f3ab36011050f6db75ab00cef47441946"}, + {file = "libcst-1.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1947790a4fd7d96bcc200a6ecaa528045fcb26a34a24030d5859c7983662289e"}, + {file = "libcst-1.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:697eabe9f5ffc40f76d6d02e693274e0a382826d0cf8183bd44e7407dfb0ab90"}, + {file = "libcst-1.5.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dc06b7c60d086ef1832aebfd31b64c3c8a645adf0c5638d6243e5838f6a9356e"}, + {file = "libcst-1.5.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:19e39cfef4316599ca20d1c821490aeb783b52e8a8543a824972a525322a85d0"}, + {file = "libcst-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:01e01c04f0641188160d3b99c6526436e93a3fbf9783dba970f9885a77ec9b38"}, + {file = "libcst-1.5.1.tar.gz", hash = "sha256:71cb294db84df9e410208009c732628e920111683c2f2b2e0c5b71b98464f365"}, ] [package.dependencies] @@ -1895,13 +1905,13 @@ et-xmlfile = "*" [[package]] name = "packaging" -version = "24.1" +version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, + {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, ] [[package]] @@ -2536,13 +2546,13 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyjwt" -version = "2.9.0" +version = "2.10.0" description = "JSON Web Token implementation in Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, - {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, + {file = "PyJWT-2.10.0-py3-none-any.whl", hash = "sha256:543b77207db656de204372350926bed5a86201c4cbff159f623f79c7bb487a15"}, + {file = "pyjwt-2.10.0.tar.gz", hash = "sha256:7628a7eb7938959ac1b26e819a1df0fd3259505627b575e4bad6d08f76db695c"}, ] [package.extras] @@ -2807,23 +2817,23 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] [[package]] name = "setuptools" -version = "75.3.0" +version = "75.5.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "setuptools-75.3.0-py3-none-any.whl", hash = "sha256:f2504966861356aa38616760c0f66568e535562374995367b4e69c7143cf6bcd"}, - {file = "setuptools-75.3.0.tar.gz", hash = "sha256:fba5dd4d766e97be1b1681d98712680ae8f2f26d7881245f2ce9e40714f1a686"}, + {file = "setuptools-75.5.0-py3-none-any.whl", hash = "sha256:87cb777c3b96d638ca02031192d40390e0ad97737e27b6b4fa831bea86f2f829"}, + {file = "setuptools-75.5.0.tar.gz", hash = "sha256:5c4ccb41111392671f02bb5f8436dfc5a9a7185e80500531b133f5775c4163ef"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"] +core = ["importlib-metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"] [[package]] name = "shellingham" @@ -2947,7 +2957,7 @@ files = [ ] [package.dependencies] -greenlet = {version = "!=0.4.17", optional = true, markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") or extra == \"asyncio\""} +greenlet = {version = "!=0.4.17", optional = true, markers = "python_version < \"3.13\" and (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\") or extra == \"asyncio\""} typing-extensions = ">=4.6.0" [package.extras] @@ -2977,13 +2987,13 @@ sqlcipher = ["sqlcipher3_binary"] [[package]] name = "starlette" -version = "0.41.2" +version = "0.41.3" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.41.2-py3-none-any.whl", hash = "sha256:fbc189474b4731cf30fcef52f18a8d070e3f3b46c6a04c97579e85e6ffca942d"}, - {file = "starlette-0.41.2.tar.gz", hash = "sha256:9834fd799d1a87fd346deb76158668cfa0b0d56f85caefe8268e2d97c3468b62"}, + {file = "starlette-0.41.3-py3-none-any.whl", hash = "sha256:44cedb2b7c77a9de33a8b74b2b90e9f50d11fcf25d8270ea525ad71a25374ff7"}, + {file = "starlette-0.41.3.tar.gz", hash = "sha256:0e4ab3d16522a255be6b28260b938eae2482f98ce5cc934cb08dce8dc3ba5835"}, ] [package.dependencies] @@ -3026,24 +3036,24 @@ typing = ["mypy (>=1.4)", "rich", "twisted"] [[package]] name = "tomli" -version = "2.0.2" +version = "2.1.0" description = "A lil' TOML parser" optional = false python-versions = ">=3.8" files = [ - {file = "tomli-2.0.2-py3-none-any.whl", hash = "sha256:2ebe24485c53d303f690b0ec092806a085f07af5a5aa1464f3931eec36caaa38"}, - {file = "tomli-2.0.2.tar.gz", hash = "sha256:d46d457a85337051c36524bc5349dd91b1877838e2979ac5ced3e710ed8a60ed"}, + {file = "tomli-2.1.0-py3-none-any.whl", hash = "sha256:a5c57c3d1c56f5ccdf89f6523458f60ef716e210fc47c4cfb188c5ba473e0391"}, + {file = "tomli-2.1.0.tar.gz", hash = "sha256:3f646cae2aec94e17d04973e4249548320197cfabdf130015d023de4b74d8ab8"}, ] [[package]] name = "typer" -version = "0.12.5" +version = "0.13.1" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" files = [ - {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, - {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, + {file = "typer-0.13.1-py3-none-any.whl", hash = "sha256:5b59580fd925e89463a29d363e0a43245ec02765bde9fb77d39e5d0f29dd7157"}, + {file = "typer-0.13.1.tar.gz", hash = "sha256:9d444cb96cc268ce6f8b94e13b4335084cef4c079998a9f4851a90229a3bd25c"}, ] [package.dependencies] @@ -3388,97 +3398,80 @@ anyio = ">=3.0.0" [[package]] name = "websockets" -version = "13.1" +version = "14.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, - {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, - {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"}, - {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"}, - {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"}, - {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"}, - {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"}, - {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"}, - {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"}, - {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"}, - {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"}, - {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"}, - {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"}, - {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"}, - {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"}, - {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"}, - {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"}, - {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"}, - {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"}, - {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"}, - {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"}, - {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"}, - {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"}, - {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"}, - {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"}, - {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"}, - {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"}, - {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"}, - {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"}, - {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"}, - {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"}, - {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"}, - {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"}, - {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"}, - {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"}, - {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"}, - {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"}, - {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"}, - {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"}, - {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"}, - {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"}, - {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"}, - {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"}, - {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"}, - {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"}, - {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"}, - {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"}, - {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"}, + {file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"}, + {file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"}, + {file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"}, + {file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"}, + {file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"}, + {file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"}, + {file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"}, + {file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"}, + {file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"}, + {file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"}, + {file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"}, + {file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"}, + {file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"}, + {file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"}, + {file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"}, + {file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"}, + {file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"}, + {file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"}, + {file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"}, + {file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"}, + {file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"}, + {file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"}, + {file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"}, + {file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"}, + {file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"}, + {file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"}, + {file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"}, + {file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"}, + {file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"}, + {file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"}, + {file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"}, + {file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"}, + {file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"}, + {file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"}, + {file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"}, + {file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"}, + {file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"}, + {file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"}, + {file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"}, ] [[package]] @@ -3542,93 +3535,93 @@ files = [ [[package]] name = "yarl" -version = "1.17.1" +version = "1.17.2" description = "Yet another URL library" optional = false python-versions = ">=3.9" files = [ - {file = "yarl-1.17.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1794853124e2f663f0ea54efb0340b457f08d40a1cef78edfa086576179c91"}, - {file = "yarl-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fbea1751729afe607d84acfd01efd95e3b31db148a181a441984ce9b3d3469da"}, - {file = "yarl-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ee427208c675f1b6e344a1f89376a9613fc30b52646a04ac0c1f6587c7e46ec"}, - {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b74ff4767d3ef47ffe0cd1d89379dc4d828d4873e5528976ced3b44fe5b0a21"}, - {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:62a91aefff3d11bf60e5956d340eb507a983a7ec802b19072bb989ce120cd948"}, - {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:846dd2e1243407133d3195d2d7e4ceefcaa5f5bf7278f0a9bda00967e6326b04"}, - {file = "yarl-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3e844be8d536afa129366d9af76ed7cb8dfefec99f5f1c9e4f8ae542279a6dc3"}, - {file = "yarl-1.17.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc7c92c1baa629cb03ecb0c3d12564f172218fb1739f54bf5f3881844daadc6d"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ae3476e934b9d714aa8000d2e4c01eb2590eee10b9d8cd03e7983ad65dfbfcba"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:c7e177c619342e407415d4f35dec63d2d134d951e24b5166afcdfd1362828e17"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64cc6e97f14cf8a275d79c5002281f3040c12e2e4220623b5759ea7f9868d6a5"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:84c063af19ef5130084db70ada40ce63a84f6c1ef4d3dbc34e5e8c4febb20822"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:482c122b72e3c5ec98f11457aeb436ae4aecca75de19b3d1de7cf88bc40db82f"}, - {file = "yarl-1.17.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:380e6c38ef692b8fd5a0f6d1fa8774d81ebc08cfbd624b1bca62a4d4af2f9931"}, - {file = "yarl-1.17.1-cp310-cp310-win32.whl", hash = "sha256:16bca6678a83657dd48df84b51bd56a6c6bd401853aef6d09dc2506a78484c7b"}, - {file = "yarl-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:561c87fea99545ef7d692403c110b2f99dced6dff93056d6e04384ad3bc46243"}, - {file = "yarl-1.17.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cbad927ea8ed814622305d842c93412cb47bd39a496ed0f96bfd42b922b4a217"}, - {file = "yarl-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fca4b4307ebe9c3ec77a084da3a9d1999d164693d16492ca2b64594340999988"}, - {file = "yarl-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff5c6771c7e3511a06555afa317879b7db8d640137ba55d6ab0d0c50425cab75"}, - {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b29beab10211a746f9846baa39275e80034e065460d99eb51e45c9a9495bcca"}, - {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a52a1ffdd824fb1835272e125385c32fd8b17fbdefeedcb4d543cc23b332d74"}, - {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58c8e9620eb82a189c6c40cb6b59b4e35b2ee68b1f2afa6597732a2b467d7e8f"}, - {file = "yarl-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d216e5d9b8749563c7f2c6f7a0831057ec844c68b4c11cb10fc62d4fd373c26d"}, - {file = "yarl-1.17.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:881764d610e3269964fc4bb3c19bb6fce55422828e152b885609ec176b41cf11"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8c79e9d7e3d8a32d4824250a9c6401194fb4c2ad9a0cec8f6a96e09a582c2cc0"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:299f11b44d8d3a588234adbe01112126010bd96d9139c3ba7b3badd9829261c3"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:cc7d768260f4ba4ea01741c1b5fe3d3a6c70eb91c87f4c8761bbcce5181beafe"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:de599af166970d6a61accde358ec9ded821234cbbc8c6413acfec06056b8e860"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2b24ec55fad43e476905eceaf14f41f6478780b870eda5d08b4d6de9a60b65b4"}, - {file = "yarl-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9fb815155aac6bfa8d86184079652c9715c812d506b22cfa369196ef4e99d1b4"}, - {file = "yarl-1.17.1-cp311-cp311-win32.whl", hash = "sha256:7615058aabad54416ddac99ade09a5510cf77039a3b903e94e8922f25ed203d7"}, - {file = "yarl-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:14bc88baa44e1f84164a392827b5defb4fa8e56b93fecac3d15315e7c8e5d8b3"}, - {file = "yarl-1.17.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:327828786da2006085a4d1feb2594de6f6d26f8af48b81eb1ae950c788d97f61"}, - {file = "yarl-1.17.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cc353841428d56b683a123a813e6a686e07026d6b1c5757970a877195f880c2d"}, - {file = "yarl-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c73df5b6e8fabe2ddb74876fb82d9dd44cbace0ca12e8861ce9155ad3c886139"}, - {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bdff5e0995522706c53078f531fb586f56de9c4c81c243865dd5c66c132c3b5"}, - {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:06157fb3c58f2736a5e47c8fcbe1afc8b5de6fb28b14d25574af9e62150fcaac"}, - {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1654ec814b18be1af2c857aa9000de7a601400bd4c9ca24629b18486c2e35463"}, - {file = "yarl-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f6595c852ca544aaeeb32d357e62c9c780eac69dcd34e40cae7b55bc4fb1147"}, - {file = "yarl-1.17.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:459e81c2fb920b5f5df744262d1498ec2c8081acdcfe18181da44c50f51312f7"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7e48cdb8226644e2fbd0bdb0a0f87906a3db07087f4de77a1b1b1ccfd9e93685"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:d9b6b28a57feb51605d6ae5e61a9044a31742db557a3b851a74c13bc61de5172"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e594b22688d5747b06e957f1ef822060cb5cb35b493066e33ceac0cf882188b7"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5f236cb5999ccd23a0ab1bd219cfe0ee3e1c1b65aaf6dd3320e972f7ec3a39da"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a2a64e62c7a0edd07c1c917b0586655f3362d2c2d37d474db1a509efb96fea1c"}, - {file = "yarl-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d0eea830b591dbc68e030c86a9569826145df485b2b4554874b07fea1275a199"}, - {file = "yarl-1.17.1-cp312-cp312-win32.whl", hash = "sha256:46ddf6e0b975cd680eb83318aa1d321cb2bf8d288d50f1754526230fcf59ba96"}, - {file = "yarl-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:117ed8b3732528a1e41af3aa6d4e08483c2f0f2e3d3d7dca7cf538b3516d93df"}, - {file = "yarl-1.17.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5d1d42556b063d579cae59e37a38c61f4402b47d70c29f0ef15cee1acaa64488"}, - {file = "yarl-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c0167540094838ee9093ef6cc2c69d0074bbf84a432b4995835e8e5a0d984374"}, - {file = "yarl-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2f0a6423295a0d282d00e8701fe763eeefba8037e984ad5de44aa349002562ac"}, - {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5b078134f48552c4d9527db2f7da0b5359abd49393cdf9794017baec7506170"}, - {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d401f07261dc5aa36c2e4efc308548f6ae943bfff20fcadb0a07517a26b196d8"}, - {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5f1ac7359e17efe0b6e5fec21de34145caef22b260e978336f325d5c84e6938"}, - {file = "yarl-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f63d176a81555984e91f2c84c2a574a61cab7111cc907e176f0f01538e9ff6e"}, - {file = "yarl-1.17.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e275792097c9f7e80741c36de3b61917aebecc08a67ae62899b074566ff8556"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:81713b70bea5c1386dc2f32a8f0dab4148a2928c7495c808c541ee0aae614d67"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:aa46dce75078fceaf7cecac5817422febb4355fbdda440db55206e3bd288cfb8"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1ce36ded585f45b1e9bb36d0ae94765c6608b43bd2e7f5f88079f7a85c61a4d3"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:2d374d70fdc36f5863b84e54775452f68639bc862918602d028f89310a034ab0"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:2d9f0606baaec5dd54cb99667fcf85183a7477f3766fbddbe3f385e7fc253299"}, - {file = "yarl-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b0341e6d9a0c0e3cdc65857ef518bb05b410dbd70d749a0d33ac0f39e81a4258"}, - {file = "yarl-1.17.1-cp313-cp313-win32.whl", hash = "sha256:2e7ba4c9377e48fb7b20dedbd473cbcbc13e72e1826917c185157a137dac9df2"}, - {file = "yarl-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:949681f68e0e3c25377462be4b658500e85ca24323d9619fdc41f68d46a1ffda"}, - {file = "yarl-1.17.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8994b29c462de9a8fce2d591028b986dbbe1b32f3ad600b2d3e1c482c93abad6"}, - {file = "yarl-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f9cbfbc5faca235fbdf531b93aa0f9f005ec7d267d9d738761a4d42b744ea159"}, - {file = "yarl-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b40d1bf6e6f74f7c0a567a9e5e778bbd4699d1d3d2c0fe46f4b717eef9e96b95"}, - {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5efe0661b9fcd6246f27957f6ae1c0eb29bc60552820f01e970b4996e016004"}, - {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5c4804e4039f487e942c13381e6c27b4b4e66066d94ef1fae3f6ba8b953f383"}, - {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5d6a6c9602fd4598fa07e0389e19fe199ae96449008d8304bf5d47cb745462e"}, - {file = "yarl-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f4c9156c4d1eb490fe374fb294deeb7bc7eaccda50e23775b2354b6a6739934"}, - {file = "yarl-1.17.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6324274b4e0e2fa1b3eccb25997b1c9ed134ff61d296448ab8269f5ac068c4c"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d8a8b74d843c2638f3864a17d97a4acda58e40d3e44b6303b8cc3d3c44ae2d29"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:7fac95714b09da9278a0b52e492466f773cfe37651cf467a83a1b659be24bf71"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c180ac742a083e109c1a18151f4dd8675f32679985a1c750d2ff806796165b55"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:578d00c9b7fccfa1745a44f4eddfdc99d723d157dad26764538fbdda37209857"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:1a3b91c44efa29e6c8ef8a9a2b583347998e2ba52c5d8280dbd5919c02dfc3b5"}, - {file = "yarl-1.17.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a7ac5b4984c468ce4f4a553df281450df0a34aefae02e58d77a0847be8d1e11f"}, - {file = "yarl-1.17.1-cp39-cp39-win32.whl", hash = "sha256:7294e38f9aa2e9f05f765b28ffdc5d81378508ce6dadbe93f6d464a8c9594473"}, - {file = "yarl-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:eb6dce402734575e1a8cc0bb1509afca508a400a57ce13d306ea2c663bad1138"}, - {file = "yarl-1.17.1-py3-none-any.whl", hash = "sha256:f1790a4b1e8e8e028c391175433b9c8122c39b46e1663228158e61e6f915bf06"}, - {file = "yarl-1.17.1.tar.gz", hash = "sha256:067a63fcfda82da6b198fa73079b1ca40b7c9b7994995b6ee38acda728b64d47"}, + {file = "yarl-1.17.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:93771146ef048b34201bfa382c2bf74c524980870bb278e6df515efaf93699ff"}, + {file = "yarl-1.17.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8281db240a1616af2f9c5f71d355057e73a1409c4648c8949901396dc0a3c151"}, + {file = "yarl-1.17.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:170ed4971bf9058582b01a8338605f4d8c849bd88834061e60e83b52d0c76870"}, + {file = "yarl-1.17.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc61b005f6521fcc00ca0d1243559a5850b9dd1e1fe07b891410ee8fe192d0c0"}, + {file = "yarl-1.17.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:871e1b47eec7b6df76b23c642a81db5dd6536cbef26b7e80e7c56c2fd371382e"}, + {file = "yarl-1.17.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a58a2f2ca7aaf22b265388d40232f453f67a6def7355a840b98c2d547bd037f"}, + {file = "yarl-1.17.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:736bb076f7299c5c55dfef3eb9e96071a795cb08052822c2bb349b06f4cb2e0a"}, + {file = "yarl-1.17.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fd51299e21da709eabcd5b2dd60e39090804431292daacbee8d3dabe39a6bc0"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:358dc7ddf25e79e1cc8ee16d970c23faee84d532b873519c5036dbb858965795"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:50d866f7b1a3f16f98603e095f24c0eeba25eb508c85a2c5939c8b3870ba2df8"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8b9c4643e7d843a0dca9cd9d610a0876e90a1b2cbc4c5ba7930a0d90baf6903f"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d63123bfd0dce5f91101e77c8a5427c3872501acece8c90df457b486bc1acd47"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:4e76381be3d8ff96a4e6c77815653063e87555981329cf8f85e5be5abf449021"}, + {file = "yarl-1.17.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:734144cd2bd633a1516948e477ff6c835041c0536cef1d5b9a823ae29899665b"}, + {file = "yarl-1.17.2-cp310-cp310-win32.whl", hash = "sha256:26bfb6226e0c157af5da16d2d62258f1ac578d2899130a50433ffee4a5dfa673"}, + {file = "yarl-1.17.2-cp310-cp310-win_amd64.whl", hash = "sha256:76499469dcc24759399accd85ec27f237d52dec300daaca46a5352fcbebb1071"}, + {file = "yarl-1.17.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:792155279dc093839e43f85ff7b9b6493a8eaa0af1f94f1f9c6e8f4de8c63500"}, + {file = "yarl-1.17.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:38bc4ed5cae853409cb193c87c86cd0bc8d3a70fd2268a9807217b9176093ac6"}, + {file = "yarl-1.17.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4a8c83f6fcdc327783bdc737e8e45b2e909b7bd108c4da1892d3bc59c04a6d84"}, + {file = "yarl-1.17.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6d5fed96f0646bfdf698b0a1cebf32b8aae6892d1bec0c5d2d6e2df44e1e2d"}, + {file = "yarl-1.17.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:782ca9c58f5c491c7afa55518542b2b005caedaf4685ec814fadfcee51f02493"}, + {file = "yarl-1.17.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ff6af03cac0d1a4c3c19e5dcc4c05252411bf44ccaa2485e20d0a7c77892ab6e"}, + {file = "yarl-1.17.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a3f47930fbbed0f6377639503848134c4aa25426b08778d641491131351c2c8"}, + {file = "yarl-1.17.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1fa68a3c921365c5745b4bd3af6221ae1f0ea1bf04b69e94eda60e57958907f"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:187df91395c11e9f9dc69b38d12406df85aa5865f1766a47907b1cc9855b6303"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:93d1c8cc5bf5df401015c5e2a3ce75a5254a9839e5039c881365d2a9dcfc6dc2"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:11d86c6145ac5c706c53d484784cf504d7d10fa407cb73b9d20f09ff986059ef"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c42774d1d1508ec48c3ed29e7b110e33f5e74a20957ea16197dbcce8be6b52ba"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8e589379ef0407b10bed16cc26e7392ef8f86961a706ade0a22309a45414d7"}, + {file = "yarl-1.17.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1056cadd5e850a1c026f28e0704ab0a94daaa8f887ece8dfed30f88befb87bb0"}, + {file = "yarl-1.17.2-cp311-cp311-win32.whl", hash = "sha256:be4c7b1c49d9917c6e95258d3d07f43cfba2c69a6929816e77daf322aaba6628"}, + {file = "yarl-1.17.2-cp311-cp311-win_amd64.whl", hash = "sha256:ac8eda86cc75859093e9ce390d423aba968f50cf0e481e6c7d7d63f90bae5c9c"}, + {file = "yarl-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:dd90238d3a77a0e07d4d6ffdebc0c21a9787c5953a508a2231b5f191455f31e9"}, + {file = "yarl-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c74f0b0472ac40b04e6d28532f55cac8090e34c3e81f118d12843e6df14d0909"}, + {file = "yarl-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4d486ddcaca8c68455aa01cf53d28d413fb41a35afc9f6594a730c9779545876"}, + {file = "yarl-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25b7e93f5414b9a983e1a6c1820142c13e1782cc9ed354c25e933aebe97fcf2"}, + {file = "yarl-1.17.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3a0baff7827a632204060f48dca9e63fbd6a5a0b8790c1a2adfb25dc2c9c0d50"}, + {file = "yarl-1.17.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:460024cacfc3246cc4d9f47a7fc860e4fcea7d1dc651e1256510d8c3c9c7cde0"}, + {file = "yarl-1.17.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5870d620b23b956f72bafed6a0ba9a62edb5f2ef78a8849b7615bd9433384171"}, + {file = "yarl-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2941756754a10e799e5b87e2319bbec481ed0957421fba0e7b9fb1c11e40509f"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9611b83810a74a46be88847e0ea616794c406dbcb4e25405e52bff8f4bee2d0a"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:cd7e35818d2328b679a13268d9ea505c85cd773572ebb7a0da7ccbca77b6a52e"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:6b981316fcd940f085f646b822c2ff2b8b813cbd61281acad229ea3cbaabeb6b"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:688058e89f512fb7541cb85c2f149c292d3fa22f981d5a5453b40c5da49eb9e8"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56afb44a12b0864d17b597210d63a5b88915d680f6484d8d202ed68ade38673d"}, + {file = "yarl-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:17931dfbb84ae18b287279c1f92b76a3abcd9a49cd69b92e946035cff06bcd20"}, + {file = "yarl-1.17.2-cp312-cp312-win32.whl", hash = "sha256:ff8d95e06546c3a8c188f68040e9d0360feb67ba8498baf018918f669f7bc39b"}, + {file = "yarl-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:4c840cc11163d3c01a9d8aad227683c48cd3e5be5a785921bcc2a8b4b758c4f3"}, + {file = "yarl-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3294f787a437cb5d81846de3a6697f0c35ecff37a932d73b1fe62490bef69211"}, + {file = "yarl-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f1e7fedb09c059efee2533119666ca7e1a2610072076926fa028c2ba5dfeb78c"}, + {file = "yarl-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:da9d3061e61e5ae3f753654813bc1cd1c70e02fb72cf871bd6daf78443e9e2b1"}, + {file = "yarl-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91c012dceadc695ccf69301bfdccd1fc4472ad714fe2dd3c5ab4d2046afddf29"}, + {file = "yarl-1.17.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f11fd61d72d93ac23718d393d2a64469af40be2116b24da0a4ca6922df26807e"}, + {file = "yarl-1.17.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46c465ad06971abcf46dd532f77560181387b4eea59084434bdff97524444032"}, + {file = "yarl-1.17.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef6eee1a61638d29cd7c85f7fd3ac7b22b4c0fabc8fd00a712b727a3e73b0685"}, + {file = "yarl-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4434b739a8a101a837caeaa0137e0e38cb4ea561f39cb8960f3b1e7f4967a3fc"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:752485cbbb50c1e20908450ff4f94217acba9358ebdce0d8106510859d6eb19a"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:17791acaa0c0f89323c57da7b9a79f2174e26d5debbc8c02d84ebd80c2b7bff8"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5c6ea72fe619fee5e6b5d4040a451d45d8175f560b11b3d3e044cd24b2720526"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db5ac3871ed76340210fe028f535392f097fb31b875354bcb69162bba2632ef4"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:7a1606ba68e311576bcb1672b2a1543417e7e0aa4c85e9e718ba6466952476c0"}, + {file = "yarl-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9bc27dd5cfdbe3dc7f381b05e6260ca6da41931a6e582267d5ca540270afeeb2"}, + {file = "yarl-1.17.2-cp313-cp313-win32.whl", hash = "sha256:52492b87d5877ec405542f43cd3da80bdcb2d0c2fbc73236526e5f2c28e6db28"}, + {file = "yarl-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:8e1bf59e035534ba4077f5361d8d5d9194149f9ed4f823d1ee29ef3e8964ace3"}, + {file = "yarl-1.17.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c556fbc6820b6e2cda1ca675c5fa5589cf188f8da6b33e9fc05b002e603e44fa"}, + {file = "yarl-1.17.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f2f44a4247461965fed18b2573f3a9eb5e2c3cad225201ee858726cde610daca"}, + {file = "yarl-1.17.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a3ede8c248f36b60227eb777eac1dbc2f1022dc4d741b177c4379ca8e75571a"}, + {file = "yarl-1.17.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2654caaf5584449d49c94a6b382b3cb4a246c090e72453493ea168b931206a4d"}, + {file = "yarl-1.17.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d41c684f286ce41fa05ab6af70f32d6da1b6f0457459a56cf9e393c1c0b2217"}, + {file = "yarl-1.17.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2270d590997445a0dc29afa92e5534bfea76ba3aea026289e811bf9ed4b65a7f"}, + {file = "yarl-1.17.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18662443c6c3707e2fc7fad184b4dc32dd428710bbe72e1bce7fe1988d4aa654"}, + {file = "yarl-1.17.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:75ac158560dec3ed72f6d604c81090ec44529cfb8169b05ae6fcb3e986b325d9"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1fee66b32e79264f428dc8da18396ad59cc48eef3c9c13844adec890cd339db5"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:585ce7cd97be8f538345de47b279b879e091c8b86d9dbc6d98a96a7ad78876a3"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:c019abc2eca67dfa4d8fb72ba924871d764ec3c92b86d5b53b405ad3d6aa56b0"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:c6e659b9a24d145e271c2faf3fa6dd1fcb3e5d3f4e17273d9e0350b6ab0fe6e2"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:d17832ba39374134c10e82d137e372b5f7478c4cceeb19d02ae3e3d1daed8721"}, + {file = "yarl-1.17.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bc3003710e335e3f842ae3fd78efa55f11a863a89a72e9a07da214db3bf7e1f8"}, + {file = "yarl-1.17.2-cp39-cp39-win32.whl", hash = "sha256:f5ffc6b7ace5b22d9e73b2a4c7305740a339fbd55301d52735f73e21d9eb3130"}, + {file = "yarl-1.17.2-cp39-cp39-win_amd64.whl", hash = "sha256:48e424347a45568413deec6f6ee2d720de2cc0385019bedf44cd93e8638aa0ed"}, + {file = "yarl-1.17.2-py3-none-any.whl", hash = "sha256:dd7abf4f717e33b7487121faf23560b3a50924f80e4bef62b22dab441ded8f3b"}, + {file = "yarl-1.17.2.tar.gz", hash = "sha256:753eaaa0c7195244c84b5cc159dc8204b7fd99f716f11198f999f2332a86b178"}, ] [package.dependencies] diff --git a/frontend/src/views/FuelExports/_schema.jsx b/frontend/src/views/FuelExports/_schema.jsx index 42afd154..c8c76002 100644 --- a/frontend/src/views/FuelExports/_schema.jsx +++ b/frontend/src/views/FuelExports/_schema.jsx @@ -66,6 +66,18 @@ export const fuelExportColDefs = (optionsData, errors) => [ cellDataType: 'text', hide: true }, + { + field: 'fuelCodeId', + cellEditor: 'agTextCellEditor', + cellDataType: 'text', + hide: true + }, + { + field: 'endUseId', + cellEditor: 'agTextCellEditor', + cellDataType: 'text', + hide: true + }, { field: 'complianceUnits', headerName: i18n.t('fuelExport:fuelExportColLabels.complianceUnits'), @@ -118,7 +130,8 @@ export const fuelExportColDefs = (optionsData, errors) => [ params.data.fuelTypeId = fuelType?.fuelTypeId params.data.fuelTypeOther = undefined params.data.fuelCategory = undefined - params.data.endUse = '' + params.data.endUseId = undefined + params.data.endUseType = undefined params.data.eer = undefined params.data.provisionOfTheAct = undefined params.data.fuelCode = undefined @@ -189,7 +202,8 @@ export const fuelExportColDefs = (optionsData, errors) => [ ?.fuelCategories?.find( (obj) => params.newValue === obj.fuelCategory )?.fuelCategoryId - params.data.endUse = undefined + params.data.endUseId = undefined + params.data.endUseType = undefined params.data.eer = undefined params.data.provisionOfTheAct = undefined params.data.fuelCode = undefined @@ -216,7 +230,7 @@ export const fuelExportColDefs = (optionsData, errors) => [ tooltipValueGetter: (p) => 'Select the fuel category from the list' }, { - field: 'endUse', + field: 'endUseType', headerComponent: RequiredHeader, headerName: i18n.t('fuelExport:fuelExportColLabels.endUse'), options: (params) => @@ -245,9 +259,27 @@ export const fuelExportColDefs = (optionsData, errors) => [ (!params.value && Select), cellStyle: (params) => cellErrorStyle(params, errors), suppressKeyboardEvent, - valueGetter: (params) => - params.colDef?.options(params).length < 1 ? 'Any' : params.data?.endUse, + valueGetter: (params) => { + return params.colDef?.cellEditorParams(params).options.length < 1 + ? 'Any' + : params.data?.endUseType?.type + }, editable: (params) => params.colDef?.options(params).length > 0, + valueSetter: (params) => { + if (params.newValue) { + const eerRatio = optionsData?.fuelTypes + ?.find((obj) => params.data.fuelType === obj.fuelType) + ?.eerRatios.filter( + (item) => + item.fuelCategory.fuelCategory === params.data.fuelCategory + ) + .find((eerRatio) => eerRatio.endUseType.type === params.newValue) + + params.data.endUseType = eerRatio.endUseType + params.data.endUseId = eerRatio.endUseType.endUseTypeId + } + return true + }, minWidth: 260 }, { @@ -442,7 +474,7 @@ export const fuelExportColDefs = (optionsData, errors) => [ eerOptions?.eerRatios.find( (item) => item.fuelCategory.fuelCategory === params.data.fuelCategory && - item.endUseType?.type === params.data.endUse + item.endUseType?.type === params.data.endUseType?.type ) if (!eer) { eer = eerOptions?.eerRatios?.find( diff --git a/frontend/src/views/FuelSupplies/AddEditFuelSupplies.jsx b/frontend/src/views/FuelSupplies/AddEditFuelSupplies.jsx index a38f8be9..8401179c 100644 --- a/frontend/src/views/FuelSupplies/AddEditFuelSupplies.jsx +++ b/frontend/src/views/FuelSupplies/AddEditFuelSupplies.jsx @@ -69,6 +69,7 @@ export const AddEditFuelSupplies = () => { const updatedRowData = data.fuelSupplies.map((item) => ({ ...item, complianceReportId, // This takes current reportId, important for versioning + compliancePeriod, fuelCategory: item.fuelCategory?.category, fuelType: item.fuelType?.fuelType, fuelTypeOther: @@ -83,7 +84,7 @@ export const AddEditFuelSupplies = () => { setRowData([{ id: uuid() }]) } }, - [data, complianceReportId] + [data, complianceReportId, compliancePeriod] ) useEffect(() => { diff --git a/frontend/src/views/FuelSupplies/_schema.jsx b/frontend/src/views/FuelSupplies/_schema.jsx index aa0587d3..f3452f33 100644 --- a/frontend/src/views/FuelSupplies/_schema.jsx +++ b/frontend/src/views/FuelSupplies/_schema.jsx @@ -443,7 +443,7 @@ export const fuelSupplyColDefs = (optionsData, errors, warnings) => [ headerName: i18n.t('fuelSupply:fuelSupplyColLabels.ciOfFuel'), editable: false, cellStyle: (params) => - StandardCellWarningAndErrors(params, errors, warnings), + StandardCellWarningAndErrors(params, errors, warnings) }, { field: 'energyDensity',