From 955a9b5bb52c57737ae86ab267927bdc3b67f1be Mon Sep 17 00:00:00 2001 From: Alex Zorkin Date: Wed, 4 Dec 2024 14:51:51 -0800 Subject: [PATCH 01/25] fix: default allocation agreement to 1 row on empty data --- frontend/src/App.jsx | 2 +- ...ts.jsx => AddEditAllocationAgreements.jsx} | 16 ++- .../AddEditAllocationAgreements.test.jsx | 133 ++++++++++++++++++ 3 files changed, 149 insertions(+), 2 deletions(-) rename frontend/src/views/AllocationAgreements/{AddAllocationAgreements.jsx => AddEditAllocationAgreements.jsx} (94%) create mode 100644 frontend/src/views/AllocationAgreements/__tests__/AddEditAllocationAgreements.test.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index f9ec1948b..0fca499df 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -32,7 +32,7 @@ import { AddEditOtherUses } from './views/OtherUses/AddEditOtherUses' import { AddEditFinalSupplyEquipments } from './views/FinalSupplyEquipments/AddEditFinalSupplyEquipments' import { AddEditFuelSupplies } from './views/FuelSupplies/AddEditFuelSupplies' import { AddEditFuelExports } from './views/FuelExports/AddEditFuelExports' -import { AddEditAllocationAgreements } from './views/AllocationAgreements/AddAllocationAgreements' +import { AddEditAllocationAgreements } from './views/AllocationAgreements/AddEditAllocationAgreements' import { logout } from '@/utils/keycloak.js' import { CompareReports } from '@/views/CompareReports/CompareReports' diff --git a/frontend/src/views/AllocationAgreements/AddAllocationAgreements.jsx b/frontend/src/views/AllocationAgreements/AddEditAllocationAgreements.jsx similarity index 94% rename from frontend/src/views/AllocationAgreements/AddAllocationAgreements.jsx rename to frontend/src/views/AllocationAgreements/AddEditAllocationAgreements.jsx index 2a9d258bf..390c792d5 100644 --- a/frontend/src/views/AllocationAgreements/AddAllocationAgreements.jsx +++ b/frontend/src/views/AllocationAgreements/AddEditAllocationAgreements.jsx @@ -71,7 +71,21 @@ export const AddEditAllocationAgreements = () => { const onGridReady = useCallback( async (params) => { setGridApi(params.api) - setRowData([...(data.allocationAgreements || { id: uuid() })]) + + if ( + Array.isArray(data.allocationAgreements) && + data.allocationAgreements.length > 0 + ) { + const updatedRowData = data.allocationAgreements.map((item) => ({ + ...item, + id: item.id || uuid() // Ensure every item has a unique ID + })) + setRowData(updatedRowData) + } else { + // If allocationAgreements is not available or empty, initialize with a single row + setRowData([{ id: uuid() }]) + } + params.api.sizeColumnsToFit() }, [data] diff --git a/frontend/src/views/AllocationAgreements/__tests__/AddEditAllocationAgreements.test.jsx b/frontend/src/views/AllocationAgreements/__tests__/AddEditAllocationAgreements.test.jsx new file mode 100644 index 000000000..ce7d72113 --- /dev/null +++ b/frontend/src/views/AllocationAgreements/__tests__/AddEditAllocationAgreements.test.jsx @@ -0,0 +1,133 @@ +// src/views/AllocationAgreements/__tests__/AddEditAllocationAgreements.test.jsx + +import React from 'react' +import { render, screen, fireEvent, waitFor } from '@testing-library/react' +import { describe, it, expect, beforeEach, vi } from 'vitest' +import { AddEditAllocationAgreements } from '../AddEditAllocationAgreements' +import * as useAllocationAgreementHook from '@/hooks/useAllocationAgreement' +import { wrapper } from '@/tests/utils/wrapper' + +// Mock react-router-dom hooks +const mockUseLocation = vi.fn() +const mockUseNavigate = vi.fn() +const mockUseParams = vi.fn() + +vi.mock('react-router-dom', () => ({ + ...vi.importActual('react-router-dom'), + useLocation: () => mockUseLocation(), + useNavigate: () => mockUseNavigate(), + useParams: () => mockUseParams() +})) + +// Mock react-i18next +vi.mock('react-i18next', () => ({ + useTranslation: () => ({ + t: (key) => key + }) +})) + +// Mock hooks related to allocation agreements +vi.mock('@/hooks/useAllocationAgreement') + +// Mock BCGridEditor component +vi.mock('@/components/BCDataGrid/BCGridEditor', () => ({ + BCGridEditor: ({ + gridRef, + alertRef, + onGridReady, + rowData, + onCellValueChanged, + onCellEditingStopped + }) => ( +
+
+ {rowData.map((row, index) => ( +
+ {row.id} +
+ ))} +
+
+ ) +})) + +describe('AddEditAllocationAgreements', () => { + beforeEach(() => { + vi.resetAllMocks() + + // Mock react-router-dom hooks with complete location object + mockUseLocation.mockReturnValue({ + pathname: '/test-path', // Include pathname to prevent undefined errors + state: {} + }) + mockUseNavigate.mockReturnValue(vi.fn()) + mockUseParams.mockReturnValue({ + complianceReportId: 'testReportId', + compliancePeriod: '2024' + }) + + // Mock useGetAllocationAgreements hook to return empty data initially + vi.mocked( + useAllocationAgreementHook.useGetAllocationAgreements + ).mockReturnValue({ + data: { allocationAgreements: [] }, + isLoading: false + }) + + // Mock useAllocationAgreementOptions hook + vi.mocked( + useAllocationAgreementHook.useAllocationAgreementOptions + ).mockReturnValue({ + data: { fuelTypes: [] }, + isLoading: false, + isFetched: true + }) + + // Mock useSaveAllocationAgreement hook + vi.mocked( + useAllocationAgreementHook.useSaveAllocationAgreement + ).mockReturnValue({ + mutateAsync: vi.fn() + }) + }) + + it('renders the component', () => { + render(, { wrapper }) + expect( + screen.getByText('allocationAgreement:addAllocationAgreementRowsTitle') + ).toBeInTheDocument() + }) + + it('initializes with at least one row in the empty state', () => { + render(, { wrapper }) + const rows = screen.getAllByTestId('grid-row') + expect(rows.length).toBe(1) // Ensure at least one row exists + }) + + it('loads data when allocationAgreements are available', async () => { + // Update the mock to return allocation agreements + vi.mocked( + useAllocationAgreementHook.useGetAllocationAgreements + ).mockReturnValue({ + data: { + allocationAgreements: [ + { allocationAgreementId: 'testId1' }, + { allocationAgreementId: 'testId2' } + ] + }, + isLoading: false + }) + + render(, { wrapper }) + + // Use findAllByTestId for asynchronous elements + const rows = await screen.findAllByTestId('grid-row') + expect(rows.length).toBe(2) + // Check that each row's textContent matches UUID format + const uuidRegex = + /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i + rows.forEach((row) => { + expect(uuidRegex.test(row.textContent)).toBe(true) + }) + }) +}) From 820caa4cf9a9f4b287739f87fa401213b7eadeb1 Mon Sep 17 00:00:00 2001 From: Daniel Haselhan Date: Mon, 9 Dec 2024 10:53:23 -0800 Subject: [PATCH 02/25] fix: Allow negative symbol in transaction amount input --- .../layouts/MainLayout/components/UserProfileActions.jsx | 1 - .../views/Transactions/components/TransactionDetails.jsx | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/layouts/MainLayout/components/UserProfileActions.jsx b/frontend/src/layouts/MainLayout/components/UserProfileActions.jsx index f5f7dbc68..37ebd0557 100644 --- a/frontend/src/layouts/MainLayout/components/UserProfileActions.jsx +++ b/frontend/src/layouts/MainLayout/components/UserProfileActions.jsx @@ -38,7 +38,6 @@ export const UserProfileActions = () => { refetchInterval: 60000 // Automatically refetch every 1 minute (60000ms) }) const notificationsCount = notificationsData?.count || 0 - console.log(notificationsData) // Call refetch whenever the route changes useEffect(() => { diff --git a/frontend/src/views/Transactions/components/TransactionDetails.jsx b/frontend/src/views/Transactions/components/TransactionDetails.jsx index 0c3b51199..81fdd1b3b 100644 --- a/frontend/src/views/Transactions/components/TransactionDetails.jsx +++ b/frontend/src/views/Transactions/components/TransactionDetails.jsx @@ -244,8 +244,11 @@ export const TransactionDetails = ({ transactionId, isEditable }) => { helperText={errors.complianceUnits?.message} value={formattedValue} onChange={(e) => { - // Remove all non-digit characters - const numericValue = e.target.value.replace(/\D/g, '') + // Remove all non-digit characters (other than - at the front) + const numericValue = e.target.value.replace( + /(?!^-)[^0-9]/g, + '' + ) // Update the form state with the raw number onChange(numericValue) }} From 62c9aac5e15d6fb77872a5d769f789272884821a Mon Sep 17 00:00:00 2001 From: Daniel Haselhan Date: Mon, 9 Dec 2024 11:55:33 -0800 Subject: [PATCH 03/25] feat: Bypass auth for health check * Add code to bypass auth check for health endpoint --- backend/lcfs/web/api/monitoring/views.py | 3 ++- backend/lcfs/web/application.py | 19 +++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/backend/lcfs/web/api/monitoring/views.py b/backend/lcfs/web/api/monitoring/views.py index 1b36caf02..c599ef0b1 100644 --- a/backend/lcfs/web/api/monitoring/views.py +++ b/backend/lcfs/web/api/monitoring/views.py @@ -4,9 +4,10 @@ @router.get("/health") -def health_check() -> None: +def health_check() -> str: """ Checks the health of a project. It returns 200 if the project is healthy. """ + return "healthy" diff --git a/backend/lcfs/web/application.py b/backend/lcfs/web/application.py index 6d31484d0..e7117a105 100644 --- a/backend/lcfs/web/application.py +++ b/backend/lcfs/web/application.py @@ -1,30 +1,26 @@ -from importlib import metadata -import structlog import logging +import uuid -import os -import debugpy +import structlog from fastapi import FastAPI, HTTPException from fastapi.exceptions import RequestValidationError -from fastapi.responses import UJSONResponse from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import UJSONResponse from prometheus_fastapi_instrumentator import Instrumentator -from starlette.middleware.authentication import AuthenticationMiddleware from starlette.authentication import ( AuthenticationBackend, AuthCredentials, UnauthenticatedUser, ) +from starlette.middleware.authentication import AuthenticationMiddleware from starlette.middleware.base import BaseHTTPMiddleware from starlette.requests import Request from starlette.responses import JSONResponse -import uuid -import contextvars -from lcfs.settings import settings from lcfs.logging_config import setup_logging, correlation_id_var -from lcfs.web.api.router import api_router from lcfs.services.keycloak.authentication import UserAuthentication +from lcfs.settings import settings +from lcfs.web.api.router import api_router from lcfs.web.exception.exception_handler import validation_exception_handler from lcfs.web.lifetime import register_shutdown_event, register_startup_event @@ -67,6 +63,9 @@ async def authenticate(self, request): if request.scope["method"] == "OPTIONS": return AuthCredentials([]), UnauthenticatedUser() + if request.url.path == "/api/health": # Skip for health check + return AuthCredentials([]), UnauthenticatedUser() + # Lazily retrieve Redis, session, and settings from app state redis_client = self.app.state.redis_client session_factory = self.app.state.db_session_factory From cfc33f418be9b7be58b8e33e2939fd0125f5f710 Mon Sep 17 00:00:00 2001 From: Hamed Valiollahi Bayeki Date: Mon, 9 Dec 2024 15:45:01 -0800 Subject: [PATCH 04/25] feat: ensure alternate email is saved in user profile --- .../versions/2024-12-09-22-33_cd8698fe40e6.py | 34 +++++++++++++++++++ backend/lcfs/db/models/user/UserProfile.py | 3 -- backend/lcfs/tests/user/test_user_repo.py | 14 ++++---- backend/lcfs/tests/user/test_user_views.py | 7 ++-- backend/lcfs/tests/user/user_payloads.py | 1 - backend/lcfs/web/api/user/repo.py | 5 ++- backend/lcfs/web/api/user/schema.py | 5 ++- backend/lcfs/web/api/user/services.py | 9 ++--- backend/lcfs/web/api/user/views.py | 27 +++++---------- frontend/src/constants/routes/apiRoutes.js | 2 +- .../components/BCeIDNotificationSettings.jsx | 2 +- .../components/NotificationSettingsForm.jsx | 19 +++++------ 12 files changed, 69 insertions(+), 59 deletions(-) create mode 100644 backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py diff --git a/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py b/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py new file mode 100644 index 000000000..d812b2a94 --- /dev/null +++ b/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py @@ -0,0 +1,34 @@ +"""Remove notifications email from user_profile + +Revision ID: cd8698fe40e6 +Revises: 26ab15f8ab18 +Create Date: 2024-12-09 22:33:29.554360 + +""" + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = "cd8698fe40e6" +down_revision = "26ab15f8ab18" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # Remove notifications_email column from user_profile table + op.drop_column("user_profile", "notifications_email") + + +def downgrade() -> None: + # Add notifications_email column to user_profile table + op.add_column( + "user_profile", + sa.Column( + "notifications_email", + sa.String(length=255), + nullable=True, + comment="Email address used for notifications", + ), + ) diff --git a/backend/lcfs/db/models/user/UserProfile.py b/backend/lcfs/db/models/user/UserProfile.py index 681d19aa0..7fecca2f8 100644 --- a/backend/lcfs/db/models/user/UserProfile.py +++ b/backend/lcfs/db/models/user/UserProfile.py @@ -26,9 +26,6 @@ class UserProfile(BaseModel, Auditable): String(150), unique=True, nullable=False, comment="keycloak Username" ) email = Column(String(255), nullable=True, comment="Primary email address") - notifications_email = Column( - String(255), nullable=True, comment="Email address used for notifications" - ) title = Column(String(100), nullable=True, comment="Professional Title") phone = Column(String(50), nullable=True, comment="Primary phone number") mobile_phone = Column(String(50), nullable=True, comment="Mobile phone number") diff --git a/backend/lcfs/tests/user/test_user_repo.py b/backend/lcfs/tests/user/test_user_repo.py index 56cb75403..a96788933 100644 --- a/backend/lcfs/tests/user/test_user_repo.py +++ b/backend/lcfs/tests/user/test_user_repo.py @@ -1,11 +1,10 @@ -from unittest.mock import AsyncMock, Mock +from unittest.mock import Mock import pytest from lcfs.db.models import UserProfile, UserLoginHistory from lcfs.web.api.user.repo import UserRepository from lcfs.tests.user.user_payloads import user_orm_model -from lcfs.web.exception.exceptions import DataNotFoundException @pytest.fixture @@ -50,14 +49,13 @@ async def test_create_login_history(dbsession, user_repo): @pytest.mark.anyio -async def test_update_notifications_email_success(dbsession, user_repo): +async def test_update_email_success(dbsession, user_repo): # Arrange: Create a user in the database user = UserProfile( keycloak_user_id="user_id_1", keycloak_email="user1@domain.com", keycloak_username="username1", email="user1@domain.com", - notifications_email=None, title="Developer", phone="1234567890", mobile_phone="0987654321", @@ -70,10 +68,10 @@ async def test_update_notifications_email_success(dbsession, user_repo): await dbsession.commit() await dbsession.refresh(user) - # Act: Update the notifications email - updated_user = await user_repo.update_notifications_email( + # Act: Update the email + updated_user = await user_repo.update_email( user_profile_id=1, email="new_email@domain.com" ) - # Assert: Check if the notifications email was updated - assert updated_user.notifications_email == "new_email@domain.com" + # Assert: Check if the email was updated + assert updated_user.email == "new_email@domain.com" diff --git a/backend/lcfs/tests/user/test_user_views.py b/backend/lcfs/tests/user/test_user_views.py index 75a228c0c..1c68b120d 100644 --- a/backend/lcfs/tests/user/test_user_views.py +++ b/backend/lcfs/tests/user/test_user_views.py @@ -468,19 +468,18 @@ async def test_track_logged_in_success(client: AsyncClient, fastapi_app, set_moc @pytest.mark.anyio -async def test_update_notifications_email_success( +async def test_update_email_success( client: AsyncClient, fastapi_app, set_mock_user, - add_models, ): set_mock_user(fastapi_app, [RoleEnum.GOVERNMENT]) # Prepare request data - request_data = {"notifications_email": "new_email@domain.com"} + request_data = {"email": "new_email@domain.com"} # Act: Send POST request to the endpoint - url = fastapi_app.url_path_for("update_notifications_email") + url = fastapi_app.url_path_for("update_email") response = await client.post(url, json=request_data) # Assert: Check response status and content diff --git a/backend/lcfs/tests/user/user_payloads.py b/backend/lcfs/tests/user/user_payloads.py index 3491cfd5e..38b9c5acc 100644 --- a/backend/lcfs/tests/user/user_payloads.py +++ b/backend/lcfs/tests/user/user_payloads.py @@ -6,7 +6,6 @@ keycloak_email="email@domain.com", keycloak_username="username", email="email@domain.com", - notifications_email=None, title="Developer", phone="1234567890", mobile_phone="0987654321", diff --git a/backend/lcfs/web/api/user/repo.py b/backend/lcfs/web/api/user/repo.py index c1906c34c..f55e0eab4 100644 --- a/backend/lcfs/web/api/user/repo.py +++ b/backend/lcfs/web/api/user/repo.py @@ -669,7 +669,7 @@ async def create_login_history(self, user: UserProfile): self.db.add(login_history) @repo_handler - async def update_notifications_email( + async def update_email( self, user_profile_id: int, email: str ) -> UserProfile: # Fetch the user profile @@ -679,8 +679,7 @@ async def update_notifications_email( result = await self.db.execute(query) user_profile = result.scalar_one_or_none() - # Update the notifications_email field - user_profile.notifications_email = email + user_profile.email = email # Flush and refresh without committing await self.db.flush() diff --git a/backend/lcfs/web/api/user/schema.py b/backend/lcfs/web/api/user/schema.py index 6cf34fe46..e773133b1 100644 --- a/backend/lcfs/web/api/user/schema.py +++ b/backend/lcfs/web/api/user/schema.py @@ -40,7 +40,6 @@ class UserBaseSchema(BaseSchema): keycloak_username: str keycloak_email: EmailStr email: Optional[EmailStr] = None - notifications_email: Optional[EmailStr] = None title: Optional[str] = None phone: Optional[str] = None first_name: Optional[str] = None @@ -97,5 +96,5 @@ class UserLoginHistoryResponseSchema(BaseSchema): pagination: PaginationResponseSchema -class UpdateNotificationsEmailSchema(BaseSchema): - notifications_email: EmailStr +class UpdateEmailSchema(BaseSchema): + email: EmailStr diff --git a/backend/lcfs/web/api/user/services.py b/backend/lcfs/web/api/user/services.py index 48bad17de..bd539f6bb 100644 --- a/backend/lcfs/web/api/user/services.py +++ b/backend/lcfs/web/api/user/services.py @@ -334,15 +334,12 @@ async def track_user_login(self, user: UserProfile): await self.repo.create_login_history(user) @service_handler - async def update_notifications_email(self, user_id: int, email: str): + async def update_email(self, user_id: int, email: str): try: - # Update the notifications_email field of the user - return await self.repo.update_notifications_email(user_id, email) - # Return the updated user - return UserBaseSchema.model_validate(user) + return await self.repo.update_email(user_id, email) except DataNotFoundException as e: logger.error(f"User not found: {e}") raise HTTPException(status_code=404, detail=str(e)) except Exception as e: - logger.error(f"Error updating notifications email: {e}") + logger.error(f"Error updating email: {e}") raise HTTPException(status_code=500, detail="Internal Server Error") diff --git a/backend/lcfs/web/api/user/views.py b/backend/lcfs/web/api/user/views.py index ff1a69aca..fab92a37f 100644 --- a/backend/lcfs/web/api/user/views.py +++ b/backend/lcfs/web/api/user/views.py @@ -1,16 +1,7 @@ from typing import List import structlog -from fastapi import ( - APIRouter, - Body, - status, - Request, - Response, - Depends, - Query, - HTTPException, -) +from fastapi import APIRouter, Body, status, Request, Response, Depends, Query from fastapi.responses import StreamingResponse from lcfs.db import dependencies @@ -23,7 +14,7 @@ UserLoginHistoryResponseSchema, UsersSchema, UserActivitiesResponseSchema, - UpdateNotificationsEmailSchema, + UpdateEmailSchema, ) from lcfs.web.api.user.services import UserServices from lcfs.web.core.decorators import view_handler @@ -255,18 +246,18 @@ async def get_all_user_login_history( @router.post( - "/update-notifications-email", - response_model=UpdateNotificationsEmailSchema, + "/update-email", + response_model=UpdateEmailSchema, status_code=status.HTTP_200_OK, ) @view_handler(["*"]) -async def update_notifications_email( +async def update_email( request: Request, - email_data: UpdateNotificationsEmailSchema = Body(...), + email_data: UpdateEmailSchema = Body(...), service: UserServices = Depends(), ): user_id = request.user.user_profile_id - email = email_data.notifications_email + email = email_data.email - user = await service.update_notifications_email(user_id, email) - return user + user = await service.update_email(user_id, email) + return UpdateEmailSchema(email=user.email) diff --git a/frontend/src/constants/routes/apiRoutes.js b/frontend/src/constants/routes/apiRoutes.js index f9c089581..3cee90b71 100644 --- a/frontend/src/constants/routes/apiRoutes.js +++ b/frontend/src/constants/routes/apiRoutes.js @@ -75,5 +75,5 @@ export const apiRoutes = { getNotificationsCount: '/notifications/count', getNotificationSubscriptions: '/notifications/subscriptions', saveNotificationSubscriptions: '/notifications/subscriptions/save', - updateNotificationsEmail: '/users/update-notifications-email' + updateNotificationsEmail: '/users/update-email' } diff --git a/frontend/src/views/Notifications/NotificationMenu/components/BCeIDNotificationSettings.jsx b/frontend/src/views/Notifications/NotificationMenu/components/BCeIDNotificationSettings.jsx index 74af3935c..d96cd56e9 100644 --- a/frontend/src/views/Notifications/NotificationMenu/components/BCeIDNotificationSettings.jsx +++ b/frontend/src/views/Notifications/NotificationMenu/components/BCeIDNotificationSettings.jsx @@ -29,7 +29,7 @@ const BCeIDNotificationSettings = () => { ) } diff --git a/frontend/src/views/Notifications/NotificationMenu/components/NotificationSettingsForm.jsx b/frontend/src/views/Notifications/NotificationMenu/components/NotificationSettingsForm.jsx index 6fd482744..5a7e0bd93 100644 --- a/frontend/src/views/Notifications/NotificationMenu/components/NotificationSettingsForm.jsx +++ b/frontend/src/views/Notifications/NotificationMenu/components/NotificationSettingsForm.jsx @@ -25,8 +25,6 @@ import { notificationTypes, notificationChannels } from '@/constants/notificationTypes' -import { useNavigate } from 'react-router-dom' -import { ROUTES } from '@/constants/routes' import MailIcon from '@mui/icons-material/Mail' import NotificationsIcon from '@mui/icons-material/Notifications' import BCButton from '@/components/BCButton' @@ -40,19 +38,18 @@ const NotificationSettingsForm = ({ initialEmail }) => { const { t } = useTranslation(['notifications']) - const navigate = useNavigate() const [isFormLoading, setIsFormLoading] = useState(false) const [message, setMessage] = useState('') const { data: subscriptionsData, isLoading: isSubscriptionsLoading } = useNotificationSubscriptions() const createSubscription = useCreateSubscription() const deleteSubscription = useDeleteSubscription() - const updateNotificationsEmail = useUpdateNotificationsEmail() + const updateEmail = useUpdateNotificationsEmail() // Validation schema const validationSchema = Yup.object().shape({ ...(showEmailField && { - notificationEmail: Yup.string() + email: Yup.string() .email(t('errors.invalidEmail')) .required(t('errors.emailRequired')) }) @@ -75,7 +72,7 @@ const NotificationSettingsForm = ({ }) } if (showEmailField && initialEmail) { - setValue('notificationEmail', initialEmail) + setValue('email', initialEmail) } }, [subscriptionsData, showEmailField, initialEmail, setValue]) @@ -122,8 +119,8 @@ const NotificationSettingsForm = ({ try { if (showEmailField) { // BCeID user, save the email address - await updateNotificationsEmail.mutateAsync({ - notifications_email: data.notificationEmail + await updateEmail.mutateAsync({ + email: data.email }) setMessage(t('messages.emailSaved')) } @@ -423,11 +420,11 @@ const NotificationSettingsForm = ({ > {showEmailField && ( - - {t('notificationsEmail')}: + + {t('email')}: ( Date: Mon, 9 Dec 2024 16:36:03 -0800 Subject: [PATCH 05/25] feat: Expand Compliance Report Test * Now include checking rounding on line 6, submitting the report, and checking the IDIR can see it --- .../ComplianceReport/ComplianceReport.test.js | 44 ++++++++++++++++++- .../ComplianceReportManagement.feature | 17 +++++-- .../e2e/Pages/User/UserCreation.test.js | 42 +++++++++--------- frontend/cypress/e2e/accessibility.cy.js | 2 +- frontend/cypress/e2e/add__edit_org.cy.js | 2 +- frontend/cypress/e2e/disclaimer_banner.cy.js | 4 +- frontend/cypress/e2e/organization.cy.js | 2 +- frontend/cypress/e2e/user_flow.cy.js | 10 ++--- 8 files changed, 87 insertions(+), 36 deletions(-) diff --git a/frontend/cypress/e2e/Pages/ComplianceReport/ComplianceReport.test.js b/frontend/cypress/e2e/Pages/ComplianceReport/ComplianceReport.test.js index e175c8f20..8e92fff86 100644 --- a/frontend/cypress/e2e/Pages/ComplianceReport/ComplianceReport.test.js +++ b/frontend/cypress/e2e/Pages/ComplianceReport/ComplianceReport.test.js @@ -2,7 +2,7 @@ import { Given, When, Then } from '@badeball/cypress-cucumber-preprocessor' const currentYear = new Date().getFullYear().toString() -Given('the supplier is on the login page', () => { +Given('the user is on the login page', () => { cy.clearAllCookies() cy.clearAllLocalStorage() cy.clearAllSessionStorage() @@ -20,7 +20,7 @@ When('the supplier logs in with valid credentials', () => { cy.getByDataTest('dashboard-container').should('exist') }) -When('the supplier navigates to the compliance reports page', () => { +When('they navigate to the compliance reports page', () => { cy.get('a[href="/compliance-reporting"]').click() }) @@ -140,3 +140,43 @@ Then('the compliance report summary includes the quantity', () => { .should('be.visible') .and('have.text', '500') }) + +When('the supplier fills out line 6', () => { + cy.get( + '[data-test="renewable-summary"] > .MuiTable-root > .MuiTableBody-root > :nth-child(6) > :nth-child(3)' + ) + .find('input') + .type('50{enter}') + .blur() +}) + +Then('it should round the amount to 25', () => { + cy.get( + '[data-test="renewable-summary"] > .MuiTable-root > .MuiTableBody-root > :nth-child(6) > :nth-child(3)' + ) + .find('input') + .should('be.visible') + .and('have.value', '25') +}) + +When('the supplier accepts the agreement', () => { + cy.get('#signing-authority-declaration').click() +}) + +When('the supplier submits the report', () => { + cy.contains('button', 'Submit report').click() + cy.get('#modal-btn-submit-report').click() + cy.wait(2000) +}) + +Then('the status should change to Submitted', () => { + cy.get('[data-test="compliance-report-status"]') + .should('be.visible') + .and('have.text', 'Status: Submitted') +}) + +Then('they see the previously submitted report', () => { + cy.get('.ag-column-first > a > .MuiBox-root') + .should('be.visible') + .and('have.text', currentYear) +}) diff --git a/frontend/cypress/e2e/Pages/ComplianceReport/ComplianceReportManagement.feature b/frontend/cypress/e2e/Pages/ComplianceReport/ComplianceReportManagement.feature index a0e9c1975..501cf3790 100644 --- a/frontend/cypress/e2e/Pages/ComplianceReport/ComplianceReportManagement.feature +++ b/frontend/cypress/e2e/Pages/ComplianceReport/ComplianceReportManagement.feature @@ -1,12 +1,23 @@ Feature: Compliance Report Management Scenario: Supplier saves a draft compliance report - Given the supplier is on the login page - When the supplier logs in with valid credentials - And the supplier navigates to the compliance reports page + Given the user is on the login page + And the supplier logs in with valid credentials + And they navigate to the compliance reports page And the supplier creates a new compliance report Then the compliance report introduction is shown When the supplier navigates to the fuel supply page And the supplier enters a valid fuel supply row And saves and returns to the report Then the compliance report summary includes the quantity + When the supplier fills out line 6 + Then it should round the amount to 25 + When the supplier accepts the agreement + And the supplier submits the report + Then the status should change to Submitted + + Scenario: Analyst logs in to review a compliance report + Given the user is on the login page + And the analyst logs in with valid credentials + And they navigate to the compliance reports page + Then they see the previously submitted report \ No newline at end of file diff --git a/frontend/cypress/e2e/Pages/User/UserCreation.test.js b/frontend/cypress/e2e/Pages/User/UserCreation.test.js index bf9b8ff25..079dd77a6 100644 --- a/frontend/cypress/e2e/Pages/User/UserCreation.test.js +++ b/frontend/cypress/e2e/Pages/User/UserCreation.test.js @@ -25,39 +25,39 @@ When('the IDIR user logs in with valid credentials', () => { }) When('the IDIR user navigates to the user creation page', () => { - cy.get('a[href="/admin"]').click() - cy.url().should('include', '/admin/users') - cy.contains('New user').click() - cy.url().should('include', '/admin/users/add-user') + cy.get('a[href="/admin"]').click() + cy.url().should('include', '/admin/users') + cy.contains('New user').click() + cy.url().should('include', '/admin/users/add-user') }) When('the IDIR user fills out the form with valid data', () => { - cy.get('input[id="firstName"]').type('John') - cy.get('input[id="lastName"]').type('Doe') - cy.get('input[id="jobTitle"]').type('Senior Analyst') - cy.get('input[id="userName"]').type('johndoe') - cy.get('input[id="keycloakEmail"]').type('john.doe@example.com') - cy.get('input[id="phone"]').type('1234567890') - cy.get('input[id="mobilePhone"]').type('0987654321') + cy.get('input[id="firstName"]').type('John') + cy.get('input[id="lastName"]').type('Doe') + cy.get('input[id="jobTitle"]').type('Senior Analyst') + cy.get('input[id="userName"]').type('johndoe') + cy.get('input[id="keycloakEmail"]').type('john.doe@example.com') + cy.get('input[id="phone"]').type('1234567890') + cy.get('input[id="mobilePhone"]').type('0987654321') - // Select the Analyst role - cy.get('input[type="radio"][value="analyst"]').check() + // Select the Analyst role + cy.get('input[type="radio"][value="analyst"]').check() }) When('the IDIR user submits the form', () => { - cy.get('button[data-test="saveUser"]').click() + cy.get('button[data-test="saveUser"]').click() }) Then('a success message is displayed', () => { - cy.get("[data-test='alert-box'] .MuiBox-root").should( - 'contain', - 'User has been successfully saved.' - ) + cy.get("[data-test='alert-box'] .MuiBox-root").should( + 'contain', + 'User has been successfully saved.' + ) }) Then('the new user appears in the user list', () => { - cy.visit('/admin/users') - cy.contains('a', Cypress.env('john.doe@example.com')).should('be.visible') + cy.visit('/admin/users') + cy.contains('a', Cypress.env('john.doe@example.com')).should('be.visible') }) // Test for validation error @@ -75,7 +75,7 @@ When('the IDIR user fills out the form with invalid data', () => { }) Then('an error message is displayed for validation', () => { - cy.get('#userName-helper-text').should('contain', 'User name is required') + cy.get('#userName-helper-text').should('contain', 'User name is required') }) // Cleanup after the test diff --git a/frontend/cypress/e2e/accessibility.cy.js b/frontend/cypress/e2e/accessibility.cy.js index 4c44b2543..44147beff 100644 --- a/frontend/cypress/e2e/accessibility.cy.js +++ b/frontend/cypress/e2e/accessibility.cy.js @@ -18,7 +18,7 @@ describe('Accessibility Tests for LCFS', () => { it('Should have no accessibility violations in the navigation bar', () => { cy.visit('/') cy.injectAxe() - cy.login( + cy.loginWith( 'idir', Cypress.env('IDIR_TEST_USER'), Cypress.env('IDIR_TEST_PASS') diff --git a/frontend/cypress/e2e/add__edit_org.cy.js b/frontend/cypress/e2e/add__edit_org.cy.js index 1cd1e136e..1a714ee3f 100644 --- a/frontend/cypress/e2e/add__edit_org.cy.js +++ b/frontend/cypress/e2e/add__edit_org.cy.js @@ -6,7 +6,7 @@ describe('Add Organization Test Suite', () => { beforeEach(() => { - cy.login( + cy.loginWith( 'idir', Cypress.env('IDIR_TEST_USER'), Cypress.env('IDIR_TEST_PASS') diff --git a/frontend/cypress/e2e/disclaimer_banner.cy.js b/frontend/cypress/e2e/disclaimer_banner.cy.js index 363cc207a..ded9a66a4 100644 --- a/frontend/cypress/e2e/disclaimer_banner.cy.js +++ b/frontend/cypress/e2e/disclaimer_banner.cy.js @@ -5,7 +5,7 @@ describe('Disclaimer Banner Visibility Test Suite', () => { context('BCeID User', () => { beforeEach(() => { - cy.login( + cy.loginWith( 'bceid', Cypress.env('BCEID_TEST_USER'), Cypress.env('BCEID_TEST_PASS') @@ -29,7 +29,7 @@ describe('Disclaimer Banner Visibility Test Suite', () => { context('IDIR User', () => { beforeEach(() => { - cy.login( + cy.loginWith( 'idir', Cypress.env('IDIR_TEST_USER'), Cypress.env('IDIR_TEST_PASS') diff --git a/frontend/cypress/e2e/organization.cy.js b/frontend/cypress/e2e/organization.cy.js index 4ca2e7f38..2ac742e9b 100644 --- a/frontend/cypress/e2e/organization.cy.js +++ b/frontend/cypress/e2e/organization.cy.js @@ -5,7 +5,7 @@ describe('Organization Test Suite', () => { beforeEach(() => { // Login and visit the page - cy.login( + cy.loginWith( 'idir', Cypress.env('IDIR_TEST_USER'), Cypress.env('IDIR_TEST_PASS') diff --git a/frontend/cypress/e2e/user_flow.cy.js b/frontend/cypress/e2e/user_flow.cy.js index e99dde6be..233823650 100644 --- a/frontend/cypress/e2e/user_flow.cy.js +++ b/frontend/cypress/e2e/user_flow.cy.js @@ -28,12 +28,12 @@ describe('User Login Test Suite', () => { describe('IDIR Login Flow', () => { it('fails login with wrong IDIR user credentials', () => { - cy.login('idir', 'wrong_username', 'wrong_password') + cy.loginWith('idir', 'wrong_username', 'wrong_password') cy.getByDataTest('main-layout-navbar').should('not.exist') }) it('completes login with IDIR user credentials', () => { - cy.login( + cy.loginWith( 'idir', Cypress.env('IDIR_TEST_USER'), Cypress.env('IDIR_TEST_PASS') @@ -42,7 +42,7 @@ describe('User Login Test Suite', () => { }) it('executes logout functionality for IDIR user', () => { - cy.login( + cy.loginWith( 'idir', Cypress.env('IDIR_TEST_USER'), Cypress.env('IDIR_TEST_PASS') @@ -53,12 +53,12 @@ describe('User Login Test Suite', () => { describe('BCeID Login Flow', () => { it('fails login with wrong BCeID user credentials', () => { - cy.login('bceid', 'wrong_username', 'wrong_password') + cy.loginWith('bceid', 'wrong_username', 'wrong_password') cy.getByDataTest('main-layout-navbar').should('not.exist') }) it('completes login with BCeID user credentials', () => { - cy.login( + cy.loginWith( 'bceid', Cypress.env('BCEID_TEST_USER'), Cypress.env('BCEID_TEST_PASS') From 4f51e107b05eb33d53f53eeab0c15c7e2310f2cb Mon Sep 17 00:00:00 2001 From: prv-proton Date: Tue, 10 Dec 2024 03:49:30 -0800 Subject: [PATCH 06/25] Fix: LCFS - BUG AG Grid copy and paste function for Fuel codes table on IDIR side not functioning #1302 --- backend/lcfs/web/api/fuel_code/repo.py | 22 +++---- backend/lcfs/web/api/fuel_code/services.py | 4 ++ backend/lcfs/web/core/decorators.py | 2 +- .../Renderers/ValidationRenderer2.jsx | 2 +- frontend/src/constants/routes/apiRoutes.js | 1 + frontend/src/hooks/useFuelCode.js | 2 +- .../FuelCodes/AddFuelCode/AddEditFuelCode.jsx | 58 ++++++++++++++++++- .../views/FuelCodes/AddFuelCode/_schema.jsx | 9 +-- 8 files changed, 76 insertions(+), 24 deletions(-) diff --git a/backend/lcfs/web/api/fuel_code/repo.py b/backend/lcfs/web/api/fuel_code/repo.py index aa0577466..7e0d0f534 100644 --- a/backend/lcfs/web/api/fuel_code/repo.py +++ b/backend/lcfs/web/api/fuel_code/repo.py @@ -396,19 +396,8 @@ async def create_fuel_code(self, fuel_code: FuelCode) -> FuelCode: """ self.db.add(fuel_code) await self.db.flush() - await self.db.refresh( - fuel_code, - [ - "fuel_code_status", - "fuel_code_prefix", - "fuel_type", - "feedstock_fuel_transport_modes", - "finished_fuel_transport_modes", - ], - ) - # Manually load nested relationships - await self.db.refresh(fuel_code.fuel_type, ["provision_1", "provision_2"]) - return fuel_code + result = await self.get_fuel_code(fuel_code.fuel_code_id) + return result @repo_handler async def get_fuel_code(self, fuel_code_id: int) -> FuelCode: @@ -593,9 +582,14 @@ async def validate_fuel_code(self, suffix: str, prefix_id: int) -> str: result = (await self.db.execute(query)).scalar_one_or_none() if result: fuel_code_main_version = suffix.split(".")[0] - return await self.get_next_available_sub_version_fuel_code_by_prefix( + suffix = await self.get_next_available_sub_version_fuel_code_by_prefix( fuel_code_main_version, prefix_id ) + if int(suffix.split(".")[1]) > 9: + return await self.get_next_available_fuel_code_by_prefix( + result.fuel_code_prefix.prefix + ) + return suffix else: return suffix diff --git a/backend/lcfs/web/api/fuel_code/services.py b/backend/lcfs/web/api/fuel_code/services.py index 039634e6a..d40fde544 100644 --- a/backend/lcfs/web/api/fuel_code/services.py +++ b/backend/lcfs/web/api/fuel_code/services.py @@ -208,6 +208,8 @@ async def convert_to_model( transport_mode_id=matching_transport_mode.transport_mode_id, ) ) + else: + raise ValueError(f"Invalid transport mode: {transport_mode}") for transport_mode in fuel_code_schema.finished_fuel_transport_mode or []: matching_transport_mode = next( @@ -221,6 +223,8 @@ async def convert_to_model( transport_mode_id=matching_transport_mode.transport_mode_id, ) ) + else: + raise ValueError(f"Invalid transport mode: {transport_mode}") return fuel_code diff --git a/backend/lcfs/web/core/decorators.py b/backend/lcfs/web/core/decorators.py index 07dc7c5ab..e67d9afca 100644 --- a/backend/lcfs/web/core/decorators.py +++ b/backend/lcfs/web/core/decorators.py @@ -215,7 +215,7 @@ async def wrapper(*args, **kwargs): return await func(*args, **kwargs) # raise the error to the view layer - except (DatabaseException, HTTPException, DataNotFoundException): + except (DatabaseException, HTTPException, DataNotFoundException, ValueError): raise # all other errors that occur in the service layer will log an error except Exception as e: diff --git a/frontend/src/components/BCDataGrid/components/Renderers/ValidationRenderer2.jsx b/frontend/src/components/BCDataGrid/components/Renderers/ValidationRenderer2.jsx index 043b127b5..5d4931865 100644 --- a/frontend/src/components/BCDataGrid/components/Renderers/ValidationRenderer2.jsx +++ b/frontend/src/components/BCDataGrid/components/Renderers/ValidationRenderer2.jsx @@ -18,7 +18,7 @@ export const ValidationRenderer2 = ({ data }) => { ) case 'error': return ( - + { ...options, mutationFn: async (fuelCodeID) => { return await client.delete( - apiRoutes.updateFuelCode.replace(':fuelCodeId', fuelCodeID) + apiRoutes.deleteFuelCode.replace(':fuelCodeId', fuelCodeID) ) } }) diff --git a/frontend/src/views/FuelCodes/AddFuelCode/AddEditFuelCode.jsx b/frontend/src/views/FuelCodes/AddFuelCode/AddEditFuelCode.jsx index 044c767a7..f4b30e304 100644 --- a/frontend/src/views/FuelCodes/AddFuelCode/AddEditFuelCode.jsx +++ b/frontend/src/views/FuelCodes/AddFuelCode/AddEditFuelCode.jsx @@ -25,6 +25,7 @@ import BCModal from '@/components/BCModal' import BCTypography from '@/components/BCTypography' import { FUEL_CODE_STATUSES } from '@/constants/statuses' import { useCurrentUser } from '@/hooks/useCurrentUser' +import Papa from 'papaparse' const AddEditFuelCodeBase = () => { const { fuelCodeID } = useParams() @@ -197,6 +198,7 @@ const AddEditFuelCodeBase = () => { } else { const res = await createFuelCode(updatedData) updatedData.fuelCodeId = res.data.fuelCodeId + updatedData.fuelSuffix = res.data.fuelSuffix } updatedData = { @@ -210,7 +212,7 @@ const AddEditFuelCodeBase = () => { }) } catch (error) { setErrors({ - [params.node.data.id]: error.response.data.errors[0].fields + [params.node.data.id]: error.response.data?.errors && error.response.data?.errors[0]?.fields }) updatedData = { @@ -229,10 +231,12 @@ const AddEditFuelCodeBase = () => { const errMsg = `Error updating row: ${ fieldLabels.length === 1 ? fieldLabels[0] : '' } ${message}` - + updatedData.validationMsg = errMsg handleError(error, errMsg) } else { - handleError(error, `Error updating row: ${error.message}`) + const errMsg = error.response?.data?.detail || error.message + updatedData.validationMsg = errMsg + handleError(error, `Error updating row: ${errMsg}`) } } @@ -241,6 +245,53 @@ const AddEditFuelCodeBase = () => { [updateFuelCode, t] ) + const handlePaste = useCallback( + (event, { api, columnApi }) => { + const newData = [] + const clipboardData = event.clipboardData || window.clipboardData + const pastedData = clipboardData.getData('text/plain') + const headerRow = api + .getAllDisplayedColumns() + .map((column) => column.colDef.field) + .filter((col) => col) + .join('\t') + const parsedData = Papa.parse(headerRow + '\n' + pastedData, { + delimiter: '\t', + header: true, + transform: (value) => { + const num = Number(value) // Attempt to convert to a number if possible + return isNaN(num) ? value : num // Return the number if valid, otherwise keep as string + }, + skipEmptyLines: true + }) + if (parsedData.data?.length < 0) { + return + } + parsedData.data.forEach((row) => { + const newRow = { ...row } + newRow.id = uuid() + newRow.prefixId = optionsData?.fuelCodePrefixes?.find(o => o.prefix === row.prefix)?.fuelCodePrefixId + newRow.fuelTypeId = optionsData?.fuelTypes?.find(o => o.fuelType === row.fuelType)?.fuelTypeId + newRow.fuelSuffix = newRow.fuelSuffix.toString() + newRow.feedstockFuelTransportMode = row.feedstockFuelTransportMode.split(',').map(item => item.trim()) + newRow.finishedFuelTransportMode = row.finishedFuelTransportMode.split(',').map(item => item.trim()) + newRow.modified = true + newData.push(newRow) + }) + const transactions = api.applyTransaction({ add: newData }) + // Trigger onCellEditingStopped event to update the row in backend. + transactions.add.forEach((node) => { + onCellEditingStopped({ + node, + oldValue: '', + newvalue: undefined, + ...api + }) + }) + }, + [onCellEditingStopped, optionsData] + ) + const duplicateFuelCode = async (params) => { const rowData = { ...params.data, @@ -327,6 +378,7 @@ const AddEditFuelCodeBase = () => { onAction={onAction} showAddRowsButton={!existingFuelCode && hasRoles(roles.analyst)} context={{ errors }} + handlePaste={handlePaste} /> {existingFuelCode?.fuelCodeStatus.status !== FUEL_CODE_STATUSES.APPROVED && ( diff --git a/frontend/src/views/FuelCodes/AddFuelCode/_schema.jsx b/frontend/src/views/FuelCodes/AddFuelCode/_schema.jsx index 3c39a6f4c..96186c584 100644 --- a/frontend/src/views/FuelCodes/AddFuelCode/_schema.jsx +++ b/frontend/src/views/FuelCodes/AddFuelCode/_schema.jsx @@ -97,9 +97,9 @@ export const fuelCodeColDefs = (optionsData, errors, isCreate, canEdit) => [ const selectedPrefix = optionsData?.fuelCodePrefixes?.find( (obj) => obj.prefix === params.newValue ) - params.data.fuelTypeId = selectedPrefix.fuelCodePrefixId + params.data.fuelCodePrefixId = selectedPrefix.fuelCodePrefixId - params.data.fuelCode = optionsData?.fuelCodePrefixes?.find( + params.data.fuelSuffix = optionsData?.fuelCodePrefixes?.find( (obj) => obj.prefix === params.newValue )?.nextFuelCode params.data.company = undefined @@ -327,12 +327,12 @@ export const fuelCodeColDefs = (optionsData, errors, isCreate, canEdit) => [ return selectedOption.fuelType } const selectedOption = optionsData?.fuelTypes?.find( - (obj) => obj.fuelType === params.data.fuel + (obj) => obj.fuelType === params.data.fuelType ) if (selectedOption) { params.data.fuelTypeId = selectedOption.fuelTypeId } - return params.data.fuel + return params.data.fuelType }, valueSetter: (params) => { if (params.newValue) { @@ -341,6 +341,7 @@ export const fuelCodeColDefs = (optionsData, errors, isCreate, canEdit) => [ ) params.data.fuelTypeId = selectedFuelType.fuelTypeId } + return params.data.fuelType }, cellEditorParams: { options: optionsData?.fuelTypes From 28ecdb9feb8a1edb816067b68c1889e1c61fef45 Mon Sep 17 00:00:00 2001 From: prv-proton Date: Tue, 10 Dec 2024 04:16:00 -0800 Subject: [PATCH 07/25] test fixes. --- backend/lcfs/tests/fuel_code/test_fuel_code_service.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/lcfs/tests/fuel_code/test_fuel_code_service.py b/backend/lcfs/tests/fuel_code/test_fuel_code_service.py index c71608031..5dce49e52 100644 --- a/backend/lcfs/tests/fuel_code/test_fuel_code_service.py +++ b/backend/lcfs/tests/fuel_code/test_fuel_code_service.py @@ -210,7 +210,7 @@ async def test_approve_fuel_code_not_found(): repo_mock.get_fuel_code.return_value = None # Act & Assert - with pytest.raises(ServiceException): + with pytest.raises(ValueError, match="Fuel code not found"): await service.approve_fuel_code(fuel_code_id) repo_mock.get_fuel_code.assert_called_once_with(fuel_code_id) @@ -229,7 +229,7 @@ async def test_approve_fuel_code_invalid_status(): repo_mock.get_fuel_code.return_value = mock_fuel_code # Act & Assert - with pytest.raises(ServiceException): + with pytest.raises(ValueError, match="Fuel code is not in Draft"): await service.approve_fuel_code(fuel_code_id) repo_mock.get_fuel_code.assert_called_once_with(fuel_code_id) From d5ebc64d024b7acf97f1b1922e862a87bb05530b Mon Sep 17 00:00:00 2001 From: prv-proton Date: Tue, 10 Dec 2024 04:17:38 -0800 Subject: [PATCH 08/25] . --- backend/lcfs/tests/other_uses/test_other_uses_services.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/lcfs/tests/other_uses/test_other_uses_services.py b/backend/lcfs/tests/other_uses/test_other_uses_services.py index 3f4705501..14b3bb518 100644 --- a/backend/lcfs/tests/other_uses/test_other_uses_services.py +++ b/backend/lcfs/tests/other_uses/test_other_uses_services.py @@ -189,7 +189,7 @@ async def test_update_other_use_not_found(other_uses_service): mock_repo.get_other_use_version_by_user = AsyncMock(return_value=None) - with pytest.raises(ServiceException): + with pytest.raises(ValueError, match="Other use not found"): await service.update_other_use(other_use_data, UserTypeEnum.SUPPLIER) From d5d2bf93f69a2477aae27a2ba20f932d8d02821b Mon Sep 17 00:00:00 2001 From: prv-proton Date: Tue, 10 Dec 2024 09:36:43 -0800 Subject: [PATCH 09/25] initial draft --- etl/database/nifi-registry-primary.mv.db | Bin 90112 -> 90112 bytes etl/nifi/conf/flow.json.gz | Bin 9539 -> 9690 bytes etl/nifi/conf/flow.xml.gz | Bin 14964 -> 15159 bytes etl/nifi_scripts/part3award.groovy | 454 +++++++++++++++++++++++ 4 files changed, 454 insertions(+) create mode 100644 etl/nifi_scripts/part3award.groovy diff --git a/etl/database/nifi-registry-primary.mv.db b/etl/database/nifi-registry-primary.mv.db index 56acc8498b5906b1d5e0a1786bb311a88c50341c..35bd40492ff57ec847675a5539359c8067eedb22 100644 GIT binary patch delta 3400 zcmeH~TWl0n7{||=)c?^x*ufY?h60}lxx;m9EYnj8ou%l*)I{>VM&n-k$1zm0j55kI6+Nzb%7 z@nv${nrL#(IcA`j1SERrXdroOFC42NIqD2iT5NAcIbFH#y!?W~q7^GwJ>&5fTYJwu zw+{XYrv~02(5}HiZcQHE@|Oo=UNmx)^ZCulz%Ky1)OaF(tSiyqyA=S(aW~;8D@LP( zEwI>p8}uW#w|1s205?Hn=c^G2y=bU60Bcb9sajVRz}H;^WpVq z>O`%*fthaY)X8qbeh@!aPv(?+W+_xez;Lqj88Y;g>G4<+dC85;gEpl zu9lt>=q`0I#6^P)-Ptjq@4^Aw&NFznJEREqIkTDCL^ZQEimr&++DOr8yC~BA@%~;t zx~Z=)Yvg^btiby9kt-o81YsW${<}%a{Q8=}btf25V?1I-lq(yO_Sz=^P)F01E$wH*q^u$EX&Z^Ezi;poJWb;{jQ-M)583z`V z4A;(bW`IVfYr^Td7$1}}3pjycg?!dNCA^y9Gk17a*R+K|jC|-z*+ez_EL;S$>eyaAD@Q0>6dmi@F4qlD9(8w^^1d;wo;B{&+O;LgHYf zKx;j^+NWm=u=GCv*!|@58{h$_qprNo1)B`tm6L?8(A=^yvn9VY6j1ot<12YW5eP*h z6cH!3z9O27F$+K$!!-xt6dKuDk5Z?dxT`-q?MI2Lm22$=UcMYM3NsZRIrbh#!6M;* z1E>npKDayt3~ND@f-&^UM`W^O z0}P<@`igYJ5VB+ePdZ5deacAH|4I~?EMtoA)pAZyj2_0;xy6iHN*L&q1nOX_(SdEH z=mUncKz@?iN$5MKGA}ln;aEbG!&TL$U|~I7tYT>mIKpFDB|{OpVnrQYS;eHnB2 zEtvZwcD^U?Cr6pr7#)0&_TQz({chU0Hx^BhVXtZ2#?*;xi>z7%FRvgdn1j0v<6ZXF zU27~KZw0`I(X3dV#U95Lac9-)Dqj;ix4(vZyX34BR-s{ksyIWw-2kFJNXz!YQ{(t| zziR$72AoS zjSteTRVrF)n3KR* z1pqhAeE?R6nqZEThXNvmK?uOkG)SDg&g$-^K>+IW0Q^+P)(wAbur+t>xp-Y;!yYoc z)Dr9sKFci_GW>`nFUKP&MBm^sEER;|zj>&ThD;;9c4y2$^qauy_xs>KTnr-)O8^8D z(TyPK>S|s6^$6^Q56q^n2amBzeQdvEG@%VqXgR z*=?%E5XERuqW7^hi+nLU0A*4;Hr(UyNhXqFNRjE!2%AWzCkFrkopE|e1F*jCSFc67 zrmfLRZ^^y0V&w0eZwfzJ)Ve~?((E)5S&}7NRMBThz9|H&R@2?^92e7UPIG9F?XBO2 zy&hq5;jc72T^K=!Ai3te50fNz>S1!i-E~s4{P`eNn9Aj5bYxjxFBm83&dm5RM@O>e zYFY}5!fbYS+Og>dPKvH-Yg-!?q9ldVdnVbyCDj#$oMp{U8d>Wl-F2B>-U*`O+M-6j z96bm{Pq3)JsfxR?4C{I4L_)xnao{ zGUbDAijD{R;}mqz0{#UYq;x-&2S6UCm5%ywqNOc@O>EeX;sOwjM^_-%LViqt95!sM zrfjS5N$+NmXkQ&6`M!xl&HmRpo}1qB8&~v9?B&2cJws0kblxa?hOUD*Yb!jnjSg@Yrt}R}S!`3%uwBG2)=Hhq&27*Dra@rm8NYhM z(lr&7Rsi@<2UiR|=%H66spJ5u0zK1}B`kZ{$DCT&%te}LG)2<&l>SAxd3Wh4r6hnC zn35(-^r8J*9NqLes!#hI(aJ3jw|7)>^lo%7XpEc4*EHd8hdpkTJ0nY};KoHM$t)*)Ve@mi^dCcb BHemn& diff --git a/etl/nifi/conf/flow.json.gz b/etl/nifi/conf/flow.json.gz index 3d193752f596216362a55b884d496abd5032dcb7..b0d30aff5c6fa3b0a943e7ba20f3f8a6b89e0c32 100644 GIT binary patch literal 9690 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

IiwPiKE3cOQqu*`q-^3|_zP7n6`b2&^Mr^YLV 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 cj~<7cz~56tDkoWmsn0D}+vKmY&$ literal 9539 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 diff --git a/etl/nifi/conf/flow.xml.gz b/etl/nifi/conf/flow.xml.gz index be40cbe0ae026a6dfc25865505fa4c0f63f0c964..60cec5bc6232dd0615dec9f9aa09301db8aad205 100644 GIT binary patch literal 15159 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 J4{>mi{}23ryo&$; delta 14914 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 + + creditTradeHistory.each { historyItem -> + try { + def statusId = getStatusId(historyItem.transfer_status, preparedData) + def uniqueKey = "${transferId}_${statusId}" + + // Check if this combination has already been processed + if (!processedEntries.contains(uniqueKey)) { + // If not processed, add to batch and mark as processed + historyStmt.setInt(1, transferId) + historyStmt.setInt(2, statusId) + historyStmt.setInt(3, historyItem.user_profile_id) + historyStmt.setTimestamp(4, toSqlTimestamp(historyItem.create_timestamp ?: '2013-01-01T00:00:00Z')) + historyStmt.addBatch() + + processedEntries.add(uniqueKey) + } + } catch (Exception e) { + log.error("Error processing history record for transfer_id: ${transferId}", e) + } + } + + // Execute batch + historyStmt.executeBatch() +} + + +def processInternalComments(Integer transferId, List internalComments, + PreparedStatement internalCommentStmt, + PreparedStatement transferInternalCommentStmt) { + if (!internalComments) return + + // Use Set to track processed IDs and avoid duplicates + def processedIds = new HashSet() + + internalComments.each { comment -> + if (!comment) return // Skip null comments + + try { + // Insert the internal comment + internalCommentStmt.setString(1, comment.credit_trade_comment ?: '') + internalCommentStmt.setString(2, getAudienceScope(comment.role_names ?: '')) + internalCommentStmt.setInt(3, comment.create_user_id ?: null) + internalCommentStmt.setTimestamp(4, toSqlTimestamp(comment.create_timestamp ?: '2013-01-01T00:00:00Z')) + + def internalCommentId = null + def commentResult = internalCommentStmt.executeQuery() + if (commentResult.next()) { + internalCommentId = commentResult.getInt('internal_comment_id') + + // Insert the transfer-comment relationship + transferInternalCommentStmt.setInt(1, transferId) + transferInternalCommentStmt.setInt(2, internalCommentId) + transferInternalCommentStmt.executeUpdate() + } + + commentResult.close() + } catch (Exception e) { + log.error("Error processing internal comment for transfer ${transferId}: ${e.getMessage()}", e) + } + } + } + +// Helper function to determine audience scope based on role names +def getAudienceScope(String roleNames) { + if (!roleNames) return 'Analyst' + + switch (true) { + case roleNames.contains('GovDirector'): + return 'Director' + case roleNames.contains('GovComplianceManager'): + return 'Compliance Manager' + default: + return 'Analyst' + } +} + +def insertInitiativeAgreement(ResultSet rs, PreparedStatement initiativeAgreementStmt, + Long toTransactionId, Map preparedData, Connection conn) { + // Check for duplicates in the `initiative_agreement` table + def initiativeAgreementId = rs.getInt('initiative_agreement_id') + def duplicateCheckStmt = conn.prepareStatement('SELECT COUNT(*) FROM initiative_agreement WHERE initiative_agreement_id = ?') + duplicateCheckStmt.setInt(1, initiativeAgreementId) + def duplicateResult = duplicateCheckStmt.executeQuery() + duplicateResult.next() + def count = duplicateResult.getInt(1) + duplicateResult.close() + duplicateCheckStmt.close() + + if (count > 0) { + log.warn("Duplicate initiative_agreement detected with initiative_agreement_id: ${initiativeAgreementId}, skipping insertion.") + return null + } + + // Proceed with insertion if no duplicate exists + def statusId = getStatusId(rs.getString('current_status'), preparedData) + initiativeAgreementStmt.setInt(1, rs.getInt('to_organization_id')) + initiativeAgreementStmt.setObject(2, toTransactionId) + initiativeAgreementStmt.setTimestamp(3, rs.getTimestamp('transaction_effective_date')) + initiativeAgreementStmt.setInt(4, rs.getInt('compliance_units')) + initiativeAgreementStmt.setString(5, rs.getString('gov_comment')) + initiativeAgreementStmt.setObject(6, statusId) + initiativeAgreementStmt.setTimestamp(7, rs.getTimestamp('create_date')) + initiativeAgreementStmt.setTimestamp(8, rs.getTimestamp('update_date')) + initiativeAgreementStmt.setInt(9, rs.getInt('create_user')) + initiativeAgreementStmt.setInt(10, rs.getInt('update_user')) + initiativeAgreementStmt.setInt(11, rs.getInt('initiative_agreement_id')) + def result = initiativeAgreementStmt.executeQuery() + return result.next() ? result.getInt('initiative_agreement_id') : null +} \ No newline at end of file From eb586fc93240a0448d361611a6bd0fd4655fb279 Mon Sep 17 00:00:00 2001 From: prv-proton Date: Tue, 10 Dec 2024 09:56:28 -0800 Subject: [PATCH 10/25] . --- etl/nifi/conf/flow.json.gz | Bin 9690 -> 9698 bytes etl/nifi/conf/flow.xml.gz | Bin 15159 -> 15166 bytes etl/nifi_scripts/part3award.groovy | 20 +++++++++----------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/etl/nifi/conf/flow.json.gz b/etl/nifi/conf/flow.json.gz index b0d30aff5c6fa3b0a943e7ba20f3f8a6b89e0c32..bee2d232f5085c963fb146f438ca8eaf2202e9c9 100644 GIT binary patch literal 9698 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 literal 9690 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

IiwPiKE3cOQqu*`q-^3|_zP7n6`b2&^Mr^YLV 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 cj~<7cz~56tDkoWmsn0D}+vKmY&$ diff --git a/etl/nifi/conf/flow.xml.gz b/etl/nifi/conf/flow.xml.gz index 60cec5bc6232dd0615dec9f9aa09301db8aad205..48f4743ef0dad60f3c551a2909e2b26843a45028 100644 GIT binary patch literal 15166 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 J4{>mi{}23ryo&$; diff --git a/etl/nifi_scripts/part3award.groovy b/etl/nifi_scripts/part3award.groovy index ffa62bb84..c8417ac4f 100644 --- a/etl/nifi_scripts/part3award.groovy +++ b/etl/nifi_scripts/part3award.groovy @@ -167,16 +167,15 @@ try { def internalCommentsJson = internalComments ? jsonSlurper.parseText(internalComments) : [] def creditTradeHistoryJson = creditTradeHistory ? jsonSlurper.parseText(creditTradeHistory) : [] - def (fromTransactionId, toTransactionId) = processTransactions(resultSet.getString('current_status'), - resultSet, - statements.transactionStmt) + def toTransactionId = processTransactions(resultSet.getString('current_status'), + resultSet, statements.transactionStmt) - def initiativeAgreementId = insertTransfer(resultSet, statements.transferStmt, - fromTransactionId, toTransactionId, preparedData, destinationConn) + def initiativeAgreementId = insertInitiativeAgreement(resultSet, statements.initiativeAgreementStmt, + toTransactionId, preparedData, destinationConn) if (initiativeAgreementId) { - processHistory(transferId, creditTradeHistoryJson, statements.historyStmt, preparedData) - processInternalComments(transferId, internalCommentsJson, statements.internalCommentStmt, + processHistory(initiativeAgreementId, creditTradeHistoryJson, statements.historyStmt, preparedData) + processInternalComments(initiativeAgreementId, internalCommentsJson, statements.internalCommentStmt, statements.transferInternalCommentStmt) } else { log.warn("Transfer not inserted for record: ${resultSet.getInt('transfer_id')}") @@ -271,7 +270,7 @@ def prepareStatements(Connection conn) { RETURNING transfer_id ''' def INSERT_TRANSFER_HISTORY_SQL = ''' - INSERT INTO transfer_history ( + INSERT INTO initiative_agreement_history ( transfer_history_id, transfer_id, transfer_status_id, user_profile_id, create_date, effective_status ) VALUES (DEFAULT, ?, ?, ?, ?, true) ''' @@ -311,7 +310,6 @@ def toSqlTimestamp(String timestampString) { } def processTransactions(String currentStatus, ResultSet rs, PreparedStatement stmt) { - def fromTransactionId = null def toTransactionId = null switch (currentStatus) { @@ -322,7 +320,7 @@ def processTransactions(String currentStatus, ResultSet rs, PreparedStatement st break } - return [fromTransactionId, toTransactionId] + return toTransactionId } def insertTransaction(PreparedStatement stmt, ResultSet rs, String action, int orgId) { @@ -337,7 +335,7 @@ def insertTransaction(PreparedStatement stmt, ResultSet rs, String action, int o return result.next() ? result.getInt('transaction_id') : null } -def processHistory(Integer transferId, List creditTradeHistory, PreparedStatement historyStmt, Map preparedData) { +def processHistory(Integer initiativeAgreementId, List creditTradeHistory, PreparedStatement historyStmt, Map preparedData) { if (!creditTradeHistory) return // Use a Set to track unique combinations of transfer_id and transfer_status From 8bfa77584021ef198077ec567f238939c269ab10 Mon Sep 17 00:00:00 2001 From: prv-proton Date: Tue, 10 Dec 2024 10:46:32 -0800 Subject: [PATCH 11/25] part3 award transactions --- etl/nifi/conf/flow.json.gz | Bin 9698 -> 9693 bytes etl/nifi/conf/flow.xml.gz | Bin 15166 -> 15230 bytes etl/nifi_scripts/part3award.groovy | 80 ++++++++++++----------------- 3 files changed, 32 insertions(+), 48 deletions(-) diff --git a/etl/nifi/conf/flow.json.gz b/etl/nifi/conf/flow.json.gz index bee2d232f5085c963fb146f438ca8eaf2202e9c9..dc08321e7f018fcb2aa84712f232ab21f29dd044 100644 GIT binary patch delta 8635 zcmV;sAw=HdOWjM58v=qLksT)j^1+cta0Wjxixm$;k-YkURH&U10@e{rM6C{x2$e;Y z+gKYxwPvE720{{Bu4^>l;9vfOYps}ZvPJ`PB9$E59&~Ex+Sz}(cRDX$7T;v~eQju5OiSlw&kF&Aq6FGQFugvWHRY_PK9p_ zT^7=lo&r06YK@gMG!7_GV(csnO@O7OLI$zpHjG&q$K0q`8pW}p zlnB7ISVcN=RsjWL5i1t4m`O)s7r78?iH%~KLLY3b99E%KE{IhSNkgY=`F@2RUyPE1 zBX6SkGXI98?m5@U%v9t)Msk$2(!9r+G1O+R2iBZV|urFzikdH*YMw)|2=vTREZRE zj$uobV3LQ1xCnzXL}C+v3S(VNK=MiAgkm5`4YfQ76u}~j4UrP4meMdbl1US~2nb#) z2}xFpScnL7rMD=u4n^L1opzK$?}qO$(EyrhP;soJCe(}}kOZLO(AXeu ztwyjsL<=1eMM)dZbSzuqjJvjj9;oca+nNkJK)dzzEjco z&b-i_Sfw5C$0bXgy*WO9b^ME;`N@~OovEK@ARG^`ACgfqq16J*WN@ANZprIOdSNqw zw7i~4c5Qt*Z&d9%IT%|nZOc;QYwZWvsT8cG{cQEMZ~hp6DCx|4<57dvR_4e7$R+Pf z3aLr0Juo~cw)Z+2kAXFN`$+~^pB9#9-#6u9y8H<3^`GZ+m0nLq*IxSr;ot_)5?F`h zbdYzTcRtCW%J)7u@ccjmc-8Eu0uuYb6zeh@Zt*wdJNY#9JUmM%{)U8gJ)PxVlL@Lg z^|Lr07cy{vR;K=Snv2Zj>iVkZNvT(wgDiBRN7jwij>sqy$@N+~}p(vUb^nA2I# z`M2p!Gdmyfx@^hBq}q~Qym}45y?^%V*8>mk3v>m^FOIx; z9$7FXK+IidvcxLpH4aGmlGuT%eXrVTksY87RGD9Y7aGwn&WTlDXC7e&U(Sn8dg<>4 z$J|G@%SPI`6_*KN%T3B)SNC#>OWKGUR(V^4wLbfUwja6N3Z_h!KF9f4__S} z^mE_L%OdA|%1VuDTIj>M2~Hyg6(Xf5M>I0@0f7>iC>cdz9VHL)#j)o(V|ByIJnPPK z!Hw{LvXE56_m2md^<}*UVm1}K17sGnmP5b{5R1teKlj4?(hKt)n8@^_cb1je4~FlP zVTp7;VHFY=>Ya_M{&l)i+j6$@`#j^z)O=aZmVfnFmwHvN*91l@NiAT4w_tL2FyYvR z0&@#EA|0SYq#prHa1)f|*4Hg`u7z?xvf6cj&miJ0=iF}1(odD2z$k~n6qVP52^s<0 zCDlwcIT3QcONVB`hp^4n zqe1#U(Q<%1os9FThOsUUUT>2HN}U+U9EM`N7e_-bL>}y~DGU*S#4Emwg)gHU~uoMPFCZcWFUp z1hM#i+PX@!0?p~ul@+Y(l(p~Ej?4M=CJ%i*_p`ZeJ>FY(MRf5#crJu_@LnHO&iR~W z789mTB(yl4^`vP#T`f#$LxyQ1#WgN}M3GjFBD2kdn0=o%)9fsFL*lfZlotoTnqk^N z=!<1)DbptUpUd~dRdy9Cd5$hTGWxSQ}ca-n}4r!<*qL={q1{^=2gpa5w zWjyD!F6G~h3@qG>2}mggQoEC=msRfZ&T8>ZB>r_?3!2q??~?zqWVi#JyaI$C-0yG+ zaGz%8(q>tjUH;?iF#YtefA1Cm@vnb=fq#?g2%3M`_ko3y5i5oD4t1&!<@6z~ohY0; zPogarJ^~gNh_eO3EeLKwupI;ymqZF}5K&YhD%eAAwBeaL$d`86D(mqq*IaJB-?mcx z`)~oRcO(~|pX7F(w;YW^g`SDzz#oUa0s}W04J^thsiNu1CsUl8@XUH8SJCm=QjLFI zr_g^5R2{ac_SEYYE*7{RUU@?n%II`(eKkbNevluN)NjKd!u0q2v5_iIZ!ga?Ua18D zWH|Qw?#OunKz>qd8<2~cjaIYMIglp|EoUsGD6(;cs0^ZJj#wHe7{~cR5-`V?ylLJ! z;yE38iGQN>|!OK0&0JN24~K%Hy4MfID=CaJS`5?6pqx!Md`8} z#lqE27F2GqwpA-X*r+uOm(KJo`_Y{A5NP1X>Y+k&yya zKkZoKVwnrhjFws!bKQTIZ-L(v zzfqDK8au_xp5;{?%GsGMZw<8|{7TaF1}$gK1#5C`&tyEHfv#mph^2@G&Tp3!@(2cc zKo)$IK$&0-Pc|{mmy3QMhWV2?n^7Xgr!1Ki$H`-=Or$X4l3MSoY4%qy+d2YzYn2KS zw_u%8spH0C$xsijjYWUkES-~n3^5R#aoQ*b5QqUe!`M9na)#lPzYLNCNXR0SR1If; zzr{S>5IYAmvX%?T{TB~9UOVvT?12}P87ow5H%O&V0M>VmXmAGJM{+3{Kto{etC0t zu-~7_;lPH0c2XioVh$Eebgvf7676<>ybtqWwELL6b0^xZB>9$h*U>J;6k{2K$s`nz z6BEsc)qf1y?RXa8+tcnAce~JT7%&+N8z3R2MU=K&w74JI?PzR080|hL@7#@cbE9TU zx$7u5#<8+iGsHCLew5;SJ(;;gxg9UPJsj$GbMQU^%H5)F7ut+l6+Oi&cmLiY#>jqNHVq;eKfMF}2Mm+I>vk*;;B3PP;b7q2ew? z3_A>lw+bGTb{|n@H_`56YTH}feK6X6SI%ka`E}RB;p~sr_^bWSSLipS^~Jb{Pbt#j zqSwCK!J0>g8~HNh$+*1kQabW~xA74lAUe)Wq2y)oR-G zS@UYvPVTLyZWIBbVbNCuAFO7Y-b4bcJ#ESc)yxNWSk zi=4042E6b&rXBcdvA=6_PFJ{Zwf+2ieckdH6>Ao3A^pxP<(pq#qV=e-IQq4D>&FvK z&R=MdYPn3y?Rlmx@Uu!Wix_db3P7_@erLRKOM32l?RXjJP6GO9?{F)ie<%U%klip*PWv*FL27yXprc#S99M?5jF2%|MK)jeluy|N@)KdUyHwY{NK}}dwBr& z@?KA^qO+5ei_7Da{exPQx&Nu2DH%G?U3v06pMMc2Z%ls7(w*Xeqi^hmzl;kAaEL`_Sks zGgJRMUBZCnnJ6jYVOgmaX6C(V$uaMK^|06K5VH9*X183$y!`BqvkKgIij}Orj`p}* zxiR}SO!e}jH>7qL-aRmJGykqq>57n90nVii^P(T;ulX&17O4^*oXqR;Z@Cls*9Ymr zB77HJS(df4-Yg~Dtlo9J^L97&?geNHH>q~HHnEWLdsFLfP{x)+`QUY`m%UQfc<%Sp z(oqF_s+!if7f>zX+3KNIwqod3VrYxz_lD*-LUhO5%y+lImu?StFvC|%d~cv#2+VVx z6fCWK$LZ{U-eMK8s~$XjP%e>o2A^hjGWpUw1w(Pt`=9fZOr#Q@coRmts^nZ4?`@RV!pXcW+pARPKVA4wFFgeeqL zMnL`4#z_AjYRi++9h#r1oyW;x(mMnJ+h11a*1mzY{AlV@|0$*sVGcSR3Jn@FiIJ2_ zA!|adofXFF)~9Hp{~89bYyEfdFaIG~z?iLnzXe+q1%c$Z?Rt68LYM2oYX?K(Fs!1W zVBTd!kqe~Kh9PSsugpgQQdYvUgwQ9BLlsKXHX^G7Ml~0R6U-0^Vl^L;gT-WETg*pv zSbN=e?I2o8VGX??N0;hv3u zlyLxqB|vA!>hIH*NgiR`hS$-I1WTUY=K$vkuebfkww#|_4w@_-;GU2ll-Z~ z;=RN&P)PpA1>LL*>VK`E(&HPy5mTXY~s#<%pY}^wR}0 zkyuuRPs1IF+X7YL+NhTbwPmrfh-fc=a0Da3wK7851j-pnX&`hP$2Z((RuPrRxX6MK zxAWl-;b$Og>( zEngM-?p`!|Lg?@I-x`Vc_JW?$a1k5m?G*e+3g~<5P`H8x0SZVgBFwc5Iq7!_g)2pX5h2)y zm3ikA53~ClyNAeUdRg-AQ{9^iNk@QmIN_jtx&KU5kU&& zZ~2l?F(+bTYC3<@vsbr=0}yA?9qs+cdVFj91l0{sP|+X^l>=gJaPnzxbz}r>80Yp0 zD*ZH`pb~-e5rZs_oYgKi`LuLAtbfNi25kmgnX3qav`e|Hp ze*SQra>!UGK^~zIaDGzN({zosj<`*C*8H_y@7zAG13gnX=1oAaBrFZdccb#&dFDVf zz$nFt?_Yk&Y>>Xs?Oi{A%XKWR4`2)#@_zPP4gejW93NE8m1h7J@DNvt&Mtk0rRo*2 zV|wMCzw$Cr??1i6vy<1oI&k|w4Skz~qJq1EAB|Yg$oyTV9~%FDR#n_-N=+OF2*(7l z%f${!8Ad21fd<$MV&}p(+-nHqEUbu8XpM7Hun;t@yLjyyJ@DawkK(j@a!+qRA@ykM zt^3xv0NE%e%*~+A++H1G zjNipFe}p$=zQIE(Pl%JBoS?5gv-7ja{C68Ys8T;h#3E}$=ePDaCQ69`zrKqKytM`~vX)MJU`ylwRx!ptjwh9l$xf3GMAB1yw_{M*s7 za*xEl#=)|=`=*6;J8o*;$te% zDcf&N z236W{=IlED?dVs(N8)bxuW#c9q;acr!L)^YC-XDT+@!#|davDKrWDknQhLK#tp1dX+K zO9MB5!K#AH&sy1o&JXBQGKW+$p9( zS!3rDR8{^`Nvnr1tghk|OP%K`4nQH-J^q0|@?C{KxbwN`{{5b&q7_#@nd4MHQUH@~ zV6bA3Vku&74B~->QksHIX+sODEFlpGmUXiAa1>ZAHeg_(H3+>}h7^Z+w5pN~$xpKP zFcU>&#?5&>V3VvH8-Ix~K#}$v2mxnEv5+8bDcEHprc|~c=3qQ<%JL?_V9YzAC2nAU z##h)s@156`CU!)2dkfeiho+ZI{yFYRW4t2QGj;+i4S#30XVOnYZ;A9=mR|Mz1IWF% zLtnyww5B&nd;ZbIUUez6zhJX{N{m1A9EN#RzYjKf{{g6in#FeYY~6T;Ew zra*|G(;Xb$BaB-f5|d#ZCx3*TudGV2+uN_^+AAgbvH>`y1|>m#HHx)))Ya%@PA;#QB9Rj|L|TL#Q637!t+Pl|919sKDOl?^Y(tO> z4yroR8XIjxEh14nmAvpa#iF2nl22MZxyLD51?tK=zZfOOQnyTOQ-65sVvY}G5x?+` z7dd9rD@RG|6i?R+ycUVv;FP&IJ2^T6t>FCN?AKR&cd=1d@QfO&38jc*90GT6j5uN5 zRmzPsIO2r2+n%eDW0X@IDNS5xPE8 zq0gWD$~=8!+AK z1r7|6`Mn$xMGg@HlOQQfV1T(5OyVdC>w)V54tumGKLG;v4lZttBILc$<)FbJ{%%~q z=l^@OHg_-ky?mN4)YiHDGdh<$;o<6rk~Ne?2BU!b z!x#c+liLWCY{e9y2#*|Z4WzAviGi%SSML1}z#x*`eK8!{B23Ip#`AGaeyLScubV}By@FcOH?klV4uP$?MrbA4<^ z&;TTXjbgimB&8PC+wY1dpj_?<_{W1l5FRp`BTFDy{#7u{iQr-jGLH)~89v)@T`;ohT=A@@Gi#E`C44kC-;%9=xcrcKPt2FQY|-}q zRYTKfy_u5c+p4sHSvz?I=kdSa4&MUjOzdv&VCek|y=38ax@27K&gbWpedE9o{)lmJ z0yXu@CyM$_o5N9Ybg0@K=J&ZdyqQ>Yfnro@1%EII1*l^&5*!R}P>qRln&}v~D#%pJ zh;qY`#sr9rNRE_?{p~N!ysN<&Nt;L}4oZ??rQ=|QWPUcfU}E9WfVj^i9G!9KCpm*o zu>lhF@s~JYTu7Pn8?rHqI5wdXjJuf4eXZf6`C7a5tFAnSTVLy@zSho;$-7DCO%CD^ z9e*ifB||*47(uUN!~+9FWSCcXqmTxzM-4iv%B7j%@v!SYqEULZv;T7MbRH$#2MOQM z8t}DM`%Rrqg1itYVkm+3{CmlMy8GS*z8u~hWDjU;xn5d}Z@!-;&`57@Ajjig`53Y9 z>Zo}9Vf@!%r^w=Yq-x7T7pn(3;K!tBfPc<3Lf`a7I+24~_;lZ`{N`#=i_9N)9-=|w zY*`+WeskdUH)ltX{09`=op%ec<^%k(V^V;YY$q8`YzCVAE(21?QK)Ll*N&8(e6qIe zx!*$t88lvL(_e9bkM+{y)J@imIWIpO(ZU+|v7W-Le)4zmcK!RyU!Ac^FpvP4M1O)X zhAo)Dm|8_`5L%+G34*}HQ6wT4*%*vz8#rz-qadRqLX3u=U~dPZ(9&5!!yx7kNH~;n zV6eUCBM&q3t|H$5aC@nn?Llb@a~+2f*b@Okl)?{b1A`b3xnLH=7+v##btn6}1f#ZR z2(|$AEdXj>{ytEOJ3Xc^nd0k#aB^^V4)P_*os(3I+B|C^997ljCuSLEQ}@C z#F{E#7301BaqL5u7-hH&SR4!OAVVZ$XQgun)0pT$%E*B216a02sYgL67@P=LL{RL2 zU8%zcaS1Au0Lc@x5THl3ny?)o=-ZxM-kx3lPPrqL*iamDWISWg&|hs1xPL;%C_pw& zglNA~2gnzIMXDMNiYRn(td_W=ID30mn#EnYq@a+>nBJ+Tey6d<%^0Zk8Fb|pFLN%T z-pi7-cl%%84tu?~`3T@S83M!jCrm+Vft)M zo5gQ8HB>yPaM!l{DoB5H^YSrUg*8qFkJZ@d191PNjm%_Mj00$RJ#K7q z-b`~VZ~JeC1;^UVLZ`d6IOmF@osAmKpa1f=00w}7omwuz3dWmRJAb(>Z}~meOjoU! z&0eOH55C`z+qh09Umm+nfa{ORFrRmjx{uyEm^yN>ml}KdcQW$-Kl+^eP0#Gnrd>`z zryANX--yg-&3_X*pSAv&gg$HZ+fe$v`EN$*^J%|-TKE6Gdpq=YPj110xbcs=B|dhZ zX?h9yevOwFefM5;-G3j>f)O%eGLjYDFCJ9cn|ZCQVKS1I>xh)^2DNzO#)k zMAv+8Pv=7Py7!FRE-vR1s`9wq%$B!u>0&Qv&RY{qcg>L3tTaq5|N?);G%<;ui=O1SlY}h%nbKX%Y zC@K7FM;u}8-$4q(2nh)s4)R?wq69ZNbGOKGB`DFRS&mB*8B@DtT6c<{=dQt{{Ppbl zUS=ndD4yKjF*$1IJn_!+YOi>?vQ1XEe9rsU7-3Y&3S|ZL(*ym^r?Qr7(E}^9@@;Vd z2|M(g-KH`}eE{P;Tk#T;HYF+nLz7`8DOjFE?Dy$a5F={>8B4|;k6L5(1)*b9LK7iZ z5h6^}`DtQ;CFmk5ZU!4avMtm+G}QPx>;N?6MxgOqN4~NRpeA3(EztaYK=bY=KL|ga N{(lo3cDk)40RZiima6~& delta 8640 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 SPkugrI{p9NX=6;oB>?~(K(pxp diff --git a/etl/nifi/conf/flow.xml.gz b/etl/nifi/conf/flow.xml.gz index 48f4743ef0dad60f3c551a2909e2b26843a45028..b0b2fe56450fce408fa42d73e6962a9aec35f65d 100644 GIT binary patch delta 14486 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 creditTradeHistory.each { historyItem -> try { - def statusId = getStatusId(historyItem.transfer_status, preparedData) - def uniqueKey = "${transferId}_${statusId}" + def statusId = getStatusId(historyItem.initiative_agreement_status, preparedData) + def uniqueKey = "${initiativeAgreementId}_${statusId}" // Check if this combination has already been processed if (!processedEntries.contains(uniqueKey)) { // If not processed, add to batch and mark as processed - historyStmt.setInt(1, transferId) + historyStmt.setInt(1, initiativeAgreementId) historyStmt.setInt(2, statusId) historyStmt.setInt(3, historyItem.user_profile_id) historyStmt.setTimestamp(4, toSqlTimestamp(historyItem.create_timestamp ?: '2013-01-01T00:00:00Z')) @@ -358,7 +345,7 @@ def processHistory(Integer initiativeAgreementId, List creditTradeHistory, Prepa processedEntries.add(uniqueKey) } } catch (Exception e) { - log.error("Error processing history record for transfer_id: ${transferId}", e) + log.error("Error processing history record for initiative_agreement_id: ${initiativeAgreementId}", e) } } @@ -367,14 +354,11 @@ def processHistory(Integer initiativeAgreementId, List creditTradeHistory, Prepa } -def processInternalComments(Integer transferId, List internalComments, +def processInternalComments(Integer initiativeAgreementId, List internalComments, PreparedStatement internalCommentStmt, - PreparedStatement transferInternalCommentStmt) { + PreparedStatement initiativeAgreementInternalCommentStmt) { if (!internalComments) return - // Use Set to track processed IDs and avoid duplicates - def processedIds = new HashSet() - internalComments.each { comment -> if (!comment) return // Skip null comments @@ -390,15 +374,15 @@ def processInternalComments(Integer transferId, List internalComments, if (commentResult.next()) { internalCommentId = commentResult.getInt('internal_comment_id') - // Insert the transfer-comment relationship - transferInternalCommentStmt.setInt(1, transferId) - transferInternalCommentStmt.setInt(2, internalCommentId) - transferInternalCommentStmt.executeUpdate() + // Insert the initiative-agreement-comment relationship + initiativeAgreementInternalCommentStmt.setInt(1, initiativeAgreementId) + initiativeAgreementInternalCommentStmt.setInt(2, internalCommentId) + initiativeAgreementInternalCommentStmt.executeUpdate() } commentResult.close() } catch (Exception e) { - log.error("Error processing internal comment for transfer ${transferId}: ${e.getMessage()}", e) + log.error("Error processing internal comment for initiative-agreement ${initiativeAgreementId}: ${e.getMessage()}", e) } } } From c8afc9e9c7c097717ec1a5b9dd25ca0943acf28a Mon Sep 17 00:00:00 2001 From: prv-proton Date: Tue, 10 Dec 2024 11:01:00 -0800 Subject: [PATCH 12/25] complete --- etl/nifi/conf/flow.json.gz | Bin 9693 -> 9911 bytes etl/nifi/conf/flow.xml.gz | Bin 15230 -> 15384 bytes etl/nifi_scripts/adminAdjTrxn.groovy | 436 ++++++++++++++++++ ...ward.groovy => initiativeAgrmtTrxn.groovy} | 0 etl/nifi_scripts/transfer.groovy | 3 - 5 files changed, 436 insertions(+), 3 deletions(-) create mode 100644 etl/nifi_scripts/adminAdjTrxn.groovy rename etl/nifi_scripts/{part3award.groovy => initiativeAgrmtTrxn.groovy} (100%) diff --git a/etl/nifi/conf/flow.json.gz b/etl/nifi/conf/flow.json.gz index dc08321e7f018fcb2aa84712f232ab21f29dd044..14cba35b9a9f147f05a26119e68413c555be1f95 100644 GIT binary patch literal 9911 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 diff --git a/etl/nifi/conf/flow.xml.gz b/etl/nifi/conf/flow.xml.gz index b0b2fe56450fce408fa42d73e6962a9aec35f65d..919f3fadf775489b7624891c58d4722a484fe2fe 100644 GIT binary patch literal 15384 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 OTeFpo3rh+d*!() + + creditTradeHistory.each { historyItem -> + try { + def statusId = getStatusId(historyItem.admin_adjustment_status, preparedData) + def uniqueKey = "${adminAdjustmentId}_${statusId}" + + // Check if this combination has already been processed + if (!processedEntries.contains(uniqueKey)) { + // If not processed, add to batch and mark as processed + historyStmt.setInt(1, adminAdjustmentId) + historyStmt.setInt(2, statusId) + historyStmt.setInt(3, historyItem.user_profile_id) + historyStmt.setTimestamp(4, toSqlTimestamp(historyItem.create_timestamp ?: '2013-01-01T00:00:00Z')) + historyStmt.addBatch() + + processedEntries.add(uniqueKey) + } + } catch (Exception e) { + log.error("Error processing history record for admin_adjustment_id: ${adminAdjustmentId}", e) + } + } + + // Execute batch + historyStmt.executeBatch() +} + + +def processInternalComments(Integer adminAdjustmentId, List internalComments, + PreparedStatement internalCommentStmt, + PreparedStatement adminAdjustmentInternalCommentStmt) { + if (!internalComments) return + + internalComments.each { comment -> + if (!comment) return // Skip null comments + + try { + // Insert the internal comment + internalCommentStmt.setString(1, comment.credit_trade_comment ?: '') + internalCommentStmt.setString(2, getAudienceScope(comment.role_names ?: '')) + internalCommentStmt.setInt(3, comment.create_user_id ?: null) + internalCommentStmt.setTimestamp(4, toSqlTimestamp(comment.create_timestamp ?: '2013-01-01T00:00:00Z')) + + def internalCommentId = null + def commentResult = internalCommentStmt.executeQuery() + if (commentResult.next()) { + internalCommentId = commentResult.getInt('internal_comment_id') + + // Insert the admin-adjustment-comment relationship + adminAdjustmentInternalCommentStmt.setInt(1, adminAdjustmentId) + adminAdjustmentInternalCommentStmt.setInt(2, internalCommentId) + adminAdjustmentInternalCommentStmt.executeUpdate() + } + + commentResult.close() + } catch (Exception e) { + log.error("Error processing internal comment for admin-adjustment ${adminAdjustmentId}: ${e.getMessage()}", e) + } + } + } + +// Helper function to determine audience scope based on role names +def getAudienceScope(String roleNames) { + if (!roleNames) return 'Analyst' + + switch (true) { + case roleNames.contains('GovDirector'): + return 'Director' + case roleNames.contains('GovComplianceManager'): + return 'Compliance Manager' + default: + return 'Analyst' + } +} + +def insertadminAdjustment(ResultSet rs, PreparedStatement adminAdjustmentStmt, + Long toTransactionId, Map preparedData, Connection conn) { + // Check for duplicates in the `admin_adjustment` table + def adminAdjustmentId = rs.getInt('admin_adjustment_id') + def duplicateCheckStmt = conn.prepareStatement('SELECT COUNT(*) FROM admin_adjustment WHERE admin_adjustment_id = ?') + duplicateCheckStmt.setInt(1, adminAdjustmentId) + def duplicateResult = duplicateCheckStmt.executeQuery() + duplicateResult.next() + def count = duplicateResult.getInt(1) + duplicateResult.close() + duplicateCheckStmt.close() + + if (count > 0) { + log.warn("Duplicate admin_adjustment detected with admin_adjustment_id: ${adminAdjustmentId}, skipping insertion.") + return null + } + + // Proceed with insertion if no duplicate exists + def statusId = getStatusId(rs.getString('current_status'), preparedData) + adminAdjustmentStmt.setInt(1, rs.getInt('to_organization_id')) + adminAdjustmentStmt.setObject(2, toTransactionId) + adminAdjustmentStmt.setTimestamp(3, rs.getTimestamp('transaction_effective_date')) + adminAdjustmentStmt.setInt(4, rs.getInt('compliance_units')) + adminAdjustmentStmt.setString(5, rs.getString('gov_comment')) + adminAdjustmentStmt.setObject(6, statusId) + adminAdjustmentStmt.setTimestamp(7, rs.getTimestamp('create_date')) + adminAdjustmentStmt.setTimestamp(8, rs.getTimestamp('update_date')) + adminAdjustmentStmt.setInt(9, rs.getInt('create_user')) + adminAdjustmentStmt.setInt(10, rs.getInt('update_user')) + adminAdjustmentStmt.setInt(11, rs.getInt('admin_adjustment_id')) + def result = adminAdjustmentStmt.executeQuery() + return result.next() ? result.getInt('admin_adjustment_id') : null +} \ No newline at end of file diff --git a/etl/nifi_scripts/part3award.groovy b/etl/nifi_scripts/initiativeAgrmtTrxn.groovy similarity index 100% rename from etl/nifi_scripts/part3award.groovy rename to etl/nifi_scripts/initiativeAgrmtTrxn.groovy diff --git a/etl/nifi_scripts/transfer.groovy b/etl/nifi_scripts/transfer.groovy index 69211d890..aa6c5362d 100644 --- a/etl/nifi_scripts/transfer.groovy +++ b/etl/nifi_scripts/transfer.groovy @@ -404,9 +404,6 @@ def processInternalComments(Integer transferId, List internalComments, PreparedStatement transferInternalCommentStmt) { if (!internalComments) return - // Use Set to track processed IDs and avoid duplicates - def processedIds = new HashSet() - internalComments.each { comment -> if (!comment) return // Skip null comments From 14a1036c5e8d8a8a19311b20312a6f2be3992037 Mon Sep 17 00:00:00 2001 From: prv-proton Date: Tue, 10 Dec 2024 11:02:43 -0800 Subject: [PATCH 13/25] . --- etl/nifi/conf/flow.json.gz | Bin 9911 -> 9904 bytes etl/nifi/conf/flow.xml.gz | Bin 15384 -> 15430 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/etl/nifi/conf/flow.json.gz b/etl/nifi/conf/flow.json.gz index 14cba35b9a9f147f05a26119e68413c555be1f95..5780d2a559a199dd8d2bf6d2dec99ced3c7111db 100644 GIT binary patch delta 9532 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 C4Jr8m From 5544e39efb5a65ab02fae0b48021eaaaaeae351e Mon Sep 17 00:00:00 2001 From: prv-proton Date: Tue, 10 Dec 2024 12:21:37 -0800 Subject: [PATCH 14/25] adding support for different date types. --- .../FuelCodes/AddFuelCode/AddEditFuelCode.jsx | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/frontend/src/views/FuelCodes/AddFuelCode/AddEditFuelCode.jsx b/frontend/src/views/FuelCodes/AddFuelCode/AddEditFuelCode.jsx index f4b30e304..6841ea7ac 100644 --- a/frontend/src/views/FuelCodes/AddFuelCode/AddEditFuelCode.jsx +++ b/frontend/src/views/FuelCodes/AddFuelCode/AddEditFuelCode.jsx @@ -212,7 +212,9 @@ const AddEditFuelCodeBase = () => { }) } catch (error) { setErrors({ - [params.node.data.id]: error.response.data?.errors && error.response.data?.errors[0]?.fields + [params.node.data.id]: + error.response.data?.errors && + error.response.data?.errors[0]?.fields }) updatedData = { @@ -258,7 +260,15 @@ const AddEditFuelCodeBase = () => { const parsedData = Papa.parse(headerRow + '\n' + pastedData, { delimiter: '\t', header: true, - transform: (value) => { + transform: (value, field) => { + // Check for date fields and format them + const dateRegex = /^\d{4}-\d{2}-\d{2}$/ // Matches YYYY-MM-DD format + if (field.toLowerCase().includes('date') && !dateRegex.test(value)) { + const parsedDate = new Date(value) + if (!isNaN(parsedDate)) { + return parsedDate.toISOString().split('T')[0] // Format as YYYY-MM-DD + } + } const num = Number(value) // Attempt to convert to a number if possible return isNaN(num) ? value : num // Return the number if valid, otherwise keep as string }, @@ -270,11 +280,19 @@ const AddEditFuelCodeBase = () => { parsedData.data.forEach((row) => { const newRow = { ...row } newRow.id = uuid() - newRow.prefixId = optionsData?.fuelCodePrefixes?.find(o => o.prefix === row.prefix)?.fuelCodePrefixId - newRow.fuelTypeId = optionsData?.fuelTypes?.find(o => o.fuelType === row.fuelType)?.fuelTypeId + newRow.prefixId = optionsData?.fuelCodePrefixes?.find( + (o) => o.prefix === row.prefix + )?.fuelCodePrefixId + newRow.fuelTypeId = optionsData?.fuelTypes?.find( + (o) => o.fuelType === row.fuelType + )?.fuelTypeId newRow.fuelSuffix = newRow.fuelSuffix.toString() - newRow.feedstockFuelTransportMode = row.feedstockFuelTransportMode.split(',').map(item => item.trim()) - newRow.finishedFuelTransportMode = row.finishedFuelTransportMode.split(',').map(item => item.trim()) + newRow.feedstockFuelTransportMode = row.feedstockFuelTransportMode + .split(',') + .map((item) => item.trim()) + newRow.finishedFuelTransportMode = row.finishedFuelTransportMode + .split(',') + .map((item) => item.trim()) newRow.modified = true newData.push(newRow) }) From a8c272fc899693e7a1637e0ca5ef0d971bb8b4de Mon Sep 17 00:00:00 2001 From: Daniel Haselhan Date: Thu, 5 Dec 2024 11:43:14 -0800 Subject: [PATCH 15/25] fix: Load EER ratio even when end use is not defined --- backend/lcfs/web/api/fuel_code/repo.py | 39 ++++++++++++++++++-------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/backend/lcfs/web/api/fuel_code/repo.py b/backend/lcfs/web/api/fuel_code/repo.py index 7e0d0f534..4cc699008 100644 --- a/backend/lcfs/web/api/fuel_code/repo.py +++ b/backend/lcfs/web/api/fuel_code/repo.py @@ -776,18 +776,35 @@ async def get_provision_of_the_act_by_name( @repo_handler async def get_energy_effectiveness_ratio( - self, fuel_type_id: int, fuel_category_id: int, end_use_type_id: int + self, fuel_type_id: int, fuel_category_id: int, end_use_type_id: Optional[int] ) -> EnergyEffectivenessRatio: + """ + Retrieves the Energy Effectiveness Ratio based on fuel type, fuel category, + and optionally the end use type. + + Args: + fuel_type_id (int): The ID of the fuel type. + fuel_category_id (int): The ID of the fuel category. + end_use_type_id (Optional[int]): The ID of the end use type (optional). - stmt = select(EnergyEffectivenessRatio).where( + Returns: + Optional[EnergyEffectivenessRatio]: The matching EnergyEffectivenessRatio record or None. + """ + conditions = [ EnergyEffectivenessRatio.fuel_type_id == fuel_type_id, EnergyEffectivenessRatio.fuel_category_id == fuel_category_id, - EnergyEffectivenessRatio.end_use_type_id == end_use_type_id, - ) + ] + + if end_use_type_id is not None: + conditions.append( + EnergyEffectivenessRatio.end_use_type_id == end_use_type_id + ) + + stmt = select(EnergyEffectivenessRatio).where(*conditions) result = await self.db.execute(stmt) - energy_density = result.scalars().first() + energy_effectiveness_ratio = result.scalars().first() - return energy_density + return energy_effectiveness_ratio @repo_handler async def get_target_carbon_intensities( @@ -848,12 +865,10 @@ async def get_standardized_fuel_data( 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.0 + 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.0 # Fetch target carbon intensity (TCI) target_ci = None From d562b138cdef601ecccb864928be7047b042e93c Mon Sep 17 00:00:00 2001 From: Daniel Haselhan Date: Fri, 6 Dec 2024 11:33:17 -0800 Subject: [PATCH 16/25] Add Tests --- .../tests/fuel_code/test_fuel_code_repo.py | 645 +++++++++++++++++- backend/lcfs/web/api/fuel_code/repo.py | 4 +- 2 files changed, 634 insertions(+), 15 deletions(-) diff --git a/backend/lcfs/tests/fuel_code/test_fuel_code_repo.py b/backend/lcfs/tests/fuel_code/test_fuel_code_repo.py index a8c4945d1..91b3d6b09 100644 --- a/backend/lcfs/tests/fuel_code/test_fuel_code_repo.py +++ b/backend/lcfs/tests/fuel_code/test_fuel_code_repo.py @@ -1,13 +1,40 @@ +from datetime import date +from unittest import mock + import pytest from unittest.mock import AsyncMock, MagicMock +from sqlalchemy.exc import NoResultFound +from sqlalchemy.orm import joinedload + from lcfs.web.api.fuel_code.repo import FuelCodeRepository from lcfs.db.models.fuel.TransportMode import TransportMode +from lcfs.db.models.fuel.FuelType import FuelType +from lcfs.db.models.fuel.FuelCategory import FuelCategory +from lcfs.db.models.fuel.UnitOfMeasure import UnitOfMeasure +from lcfs.db.models.fuel.ExpectedUseType import ExpectedUseType +from lcfs.db.models.fuel.FuelCode import FuelCode +from lcfs.db.models.fuel.FuelCodePrefix import FuelCodePrefix +from lcfs.db.models.fuel.FuelCodeStatus import FuelCodeStatus, FuelCodeStatusEnum +from lcfs.db.models.fuel.EnergyDensity import EnergyDensity +from lcfs.db.models.fuel.EnergyEffectivenessRatio import EnergyEffectivenessRatio +from lcfs.db.models.fuel.ProvisionOfTheAct import ProvisionOfTheAct +from lcfs.db.models.fuel.AdditionalCarbonIntensity import AdditionalCarbonIntensity +from lcfs.db.models.fuel.TargetCarbonIntensity import TargetCarbonIntensity +from lcfs.db.models.compliance.CompliancePeriod import CompliancePeriod +from lcfs.web.exception.exceptions import DatabaseException @pytest.fixture def mock_db(): """Fixture for mocking the database session.""" - return AsyncMock() + mock_session = AsyncMock() + mock_session.execute = AsyncMock() + mock_session.get_one = AsyncMock() + mock_session.add = MagicMock() + mock_session.flush = AsyncMock() + mock_session.refresh = AsyncMock() + mock_session.scalar = AsyncMock() + return mock_session @pytest.fixture @@ -18,21 +45,613 @@ def fuel_code_repo(mock_db): return repo +@pytest.fixture +def valid_fuel_code(): + """Fixture for creating a repository with a mocked database.""" + fc = FuelCode( + fuel_code_id=5, + fuel_suffix="105.0", + prefix_id=1, # Assuming prefix_id=1 exists + fuel_type_id=1, # Assuming fuel_type_id=1 exists + company="Test Company", + contact_name="Test Contact", + contact_email="test@example.com", + carbon_intensity=50.00, + edrms="EDRMS-001", + application_date=date.today(), + feedstock="Corn", + feedstock_location="USA", + feedstock_misc="", + fuel_production_facility_city="CityA", + fuel_production_facility_province_state="ProvinceA", + fuel_production_facility_country="CountryA", + last_updated=date.today(), + ) + return fc + + @pytest.mark.anyio -async def test_get_transport_mode_by_name(fuel_code_repo, mock_db): - # Define the test transport mode - transport_mode_name = "Truck" - mock_transport_mode = TransportMode(transport_mode_id=1, transport_mode="Truck") +async def test_get_fuel_types(fuel_code_repo, mock_db): + mock_fuel_type = FuelType(fuel_type_id=1, fuel_type="Diesel") + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [mock_fuel_type] - # Mock the database query result - mock_db.execute.return_value.scalar_one = MagicMock() - mock_db.execute.return_value.scalar_one.return_value = mock_transport_mode + mock_db.execute.return_value = mock_result + result = await fuel_code_repo.get_fuel_types() + assert len(result) == 1 + assert result[0] == mock_fuel_type + mock_db.execute.assert_called_once() - # Call the repository method - result = await fuel_code_repo.get_transport_mode_by_name(transport_mode_name) - # Assert the result matches the mock data - assert result == mock_transport_mode +@pytest.mark.anyio +async def test_get_formatted_fuel_types(fuel_code_repo, mock_db): + # Setup mock data + mock_fuel_type = FuelType( + fuel_type_id=1, + fuel_type="Diesel", + default_carbon_intensity=80.0, + units="gCO2e/MJ", + unrecognized=False, + ) + mock_result = MagicMock() + mock_result.unique.return_value.scalars.return_value.all.return_value = [ + mock_fuel_type + ] + mock_db.execute.return_value = mock_result - # Ensure the database query was called + result = await fuel_code_repo.get_formatted_fuel_types() + assert len(result) == 1 + assert result[0]["fuel_type"] == "Diesel" mock_db.execute.assert_called_once() + + +@pytest.mark.anyio +async def test_get_fuel_type_by_name_found(fuel_code_repo, mock_db): + mock_fuel_type = FuelType(fuel_type_id=2, fuel_type="Gasoline") + mock_result = MagicMock() + mock_result.scalars.return_value.first.return_value = mock_fuel_type + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_fuel_type_by_name("Gasoline") + assert result == mock_fuel_type + + +@pytest.mark.anyio +async def test_get_fuel_type_by_name_not_found(fuel_code_repo, mock_db): + mock_result = MagicMock() + mock_result.scalars.return_value.first.return_value = None + mock_db.execute.return_value = mock_result + + with pytest.raises(DatabaseException): + await fuel_code_repo.get_fuel_type_by_name("Nonexistent") + + +@pytest.mark.anyio +async def test_get_fuel_type_by_id_found(fuel_code_repo, mock_db): + mock_fuel_type = FuelType(fuel_type_id=3, fuel_type="Biofuel") + mock_db.get_one.return_value = mock_fuel_type + + result = await fuel_code_repo.get_fuel_type_by_id(3) + assert result == mock_fuel_type + mock_db.get_one.assert_called_once() + + +@pytest.mark.anyio +async def test_get_fuel_type_by_id_not_found(fuel_code_repo, mock_db): + mock_db.get_one.return_value = None + with pytest.raises(DatabaseException): + await fuel_code_repo.get_fuel_type_by_id(999) + + +@pytest.mark.anyio +async def test_get_fuel_categories(fuel_code_repo, mock_db): + mock_fc = FuelCategory(fuel_category_id=1, category="Renewable") + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [mock_fc] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_fuel_categories() + assert len(result) == 1 + assert result[0] == mock_fc + + +@pytest.mark.anyio +async def test_get_fuel_category_by_name(fuel_code_repo, mock_db): + mock_fc = FuelCategory(fuel_category_id=2, category="Fossil") + mock_result = MagicMock() + mock_result.scalar_one_or_none.return_value = mock_fc + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_fuel_category_by_name("Fossil") + assert result == mock_fc + + +@pytest.mark.anyio +async def test_get_transport_modes(fuel_code_repo, mock_db): + mock_tm = TransportMode(transport_mode_id=1, transport_mode="Truck") + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [mock_tm] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_transport_modes() + assert len(result) == 1 + assert result[0] == mock_tm + + +@pytest.mark.anyio +async def test_get_transport_mode(fuel_code_repo, mock_db): + mock_tm = TransportMode(transport_mode_id=10, transport_mode="Ship") + mock_db.scalar.return_value = mock_tm + + result = await fuel_code_repo.get_transport_mode(10) + assert result == mock_tm + mock_db.scalar.assert_called_once() + + +@pytest.mark.anyio +async def test_get_transport_mode_by_name_found(fuel_code_repo, mock_db): + mock_tm = TransportMode(transport_mode_id=1, transport_mode="Truck") + mock_result = MagicMock() + mock_result.scalar_one.return_value = mock_tm + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_transport_mode_by_name("Truck") + assert result == mock_tm + + +@pytest.mark.anyio +async def test_get_transport_mode_by_name_not_found(fuel_code_repo, mock_db): + mock_result = MagicMock() + mock_result.scalar_one.side_effect = NoResultFound + mock_db.execute.return_value = mock_result + + with pytest.raises(DatabaseException): + await fuel_code_repo.get_transport_mode_by_name("NonexistentMode") + + +@pytest.mark.anyio +async def test_get_fuel_code_prefixes(fuel_code_repo, mock_db): + mock_prefix = FuelCodePrefix(fuel_code_prefix_id=1, prefix="BC") + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [mock_prefix] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_fuel_code_prefixes() + assert len(result) == 1 + assert result[0] == mock_prefix + + +@pytest.mark.anyio +async def test_get_fuel_code_prefix(fuel_code_repo, mock_db): + mock_prefix = FuelCodePrefix(fuel_code_prefix_id=2, prefix="AB") + mock_db.get_one.return_value = mock_prefix + + result = await fuel_code_repo.get_fuel_code_prefix(2) + assert result == mock_prefix + + +@pytest.mark.anyio +async def test_get_fuel_status_by_status(fuel_code_repo, mock_db): + mock_status = FuelCodeStatus( + fuel_code_status_id=1, status=FuelCodeStatusEnum.Approved + ) + mock_result = MagicMock() + mock_result.scalar.return_value = mock_status + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_fuel_status_by_status(FuelCodeStatusEnum.Approved) + assert result == mock_status + + +@pytest.mark.anyio +async def test_get_energy_densities(fuel_code_repo, mock_db): + ed = EnergyDensity(energy_density_id=1, density=35.0) + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [ed] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_energy_densities() + assert len(result) == 1 + assert result[0] == ed + + +@pytest.mark.anyio +async def test_get_energy_density(fuel_code_repo, mock_db): + ed = EnergyDensity(energy_density_id=2, density=40.0) + mock_result = MagicMock() + mock_result.scalars.return_value.first.return_value = ed + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_energy_density(10) + assert result == ed + + +@pytest.mark.anyio +async def test_get_energy_effectiveness_ratios(fuel_code_repo, mock_db): + eer = EnergyEffectivenessRatio(eer_id=1, ratio=2.0) + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [eer] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_energy_effectiveness_ratios() + assert len(result) == 1 + assert result[0] == eer + + +@pytest.mark.anyio +async def test_get_units_of_measure(fuel_code_repo, mock_db): + uom = UnitOfMeasure(uom_id=1, name="gCO2e/MJ") + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [uom] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_units_of_measure() + assert len(result) == 1 + assert result[0] == uom + + +@pytest.mark.anyio +async def test_get_expected_use_types(fuel_code_repo, mock_db): + eut = ExpectedUseType(expected_use_type_id=1, name="Vehicle") + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [eut] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_expected_use_types() + assert len(result) == 1 + assert result[0] == eut + + +@pytest.mark.anyio +async def test_get_expected_use_type_by_name(fuel_code_repo, mock_db): + eut = ExpectedUseType(expected_use_type_id=2, name="Heating") + mock_result = MagicMock() + mock_result.scalar_one_or_none.return_value = eut + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_expected_use_type_by_name("Heating") + assert result == eut + + +@pytest.mark.anyio +async def test_get_fuel_codes_paginated(fuel_code_repo, mock_db): + fc = FuelCode(fuel_code_id=1, fuel_suffix="101.0") + mock_db.execute.side_effect = [ + MagicMock(scalar=MagicMock(return_value=FuelCodeStatus())), + MagicMock(scalar=MagicMock(return_value=1)), + MagicMock( + unique=MagicMock( + return_value=MagicMock( + scalars=MagicMock( + return_value=MagicMock(all=MagicMock(return_value=[fc])) + ) + ) + ) + ), + ] + pagination = MagicMock(page=1, size=10, filters=[], sort_orders=[]) + result, count = await fuel_code_repo.get_fuel_codes_paginated(pagination) + assert len(result) == 1 + assert result[0] == fc + assert count == 1 + + +@pytest.mark.anyio +async def test_get_fuel_code_statuses(fuel_code_repo, mock_db): + fcs = FuelCodeStatus(fuel_code_status_id=1, status=FuelCodeStatusEnum.Approved) + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [fcs] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_fuel_code_statuses() + assert len(result) == 1 + assert result[0] == fcs + + +@pytest.mark.anyio +async def test_create_fuel_code(fuel_code_repo, mock_db, valid_fuel_code): + mock_db.flush = AsyncMock() + mock_db.scalar.return_value = valid_fuel_code + + result = await fuel_code_repo.create_fuel_code(valid_fuel_code) + assert result == valid_fuel_code + mock_db.add.assert_called_once_with(valid_fuel_code) + + +@pytest.mark.anyio +async def test_get_fuel_code(fuel_code_repo, mock_db, valid_fuel_code): + mock_db.scalar.return_value = valid_fuel_code + result = await fuel_code_repo.get_fuel_code(1) + assert result == valid_fuel_code + + +@pytest.mark.anyio +async def test_get_fuel_code_status_enum(fuel_code_repo, mock_db): + fcs = FuelCodeStatus(fuel_code_status_id=2, status=FuelCodeStatusEnum.Deleted) + mock_db.scalar.return_value = fcs + result = await fuel_code_repo.get_fuel_code_status(FuelCodeStatusEnum.Deleted) + assert result == fcs + + +@pytest.mark.anyio +async def test_update_fuel_code(fuel_code_repo, mock_db, valid_fuel_code): + mock_db.flush = AsyncMock() + mock_db.refresh = AsyncMock() + updated = await fuel_code_repo.update_fuel_code(valid_fuel_code) + assert updated.fuel_code_id == 5 + + +@pytest.mark.anyio +async def test_delete_fuel_code(fuel_code_repo, mock_db): + mock_delete_status = FuelCodeStatus( + fuel_code_status_id=3, status=FuelCodeStatusEnum.Deleted + ) + mock_execute_result = MagicMock() + mock_execute_result.scalar.return_value = mock_delete_status + mock_db.execute.return_value = mock_execute_result + + mock_db.flush = AsyncMock() + + await fuel_code_repo.delete_fuel_code(10) + mock_db.execute.assert_awaited() # Check that execute was awaited + + +@pytest.mark.anyio +async def test_get_distinct_company_names(fuel_code_repo, mock_db): + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = ["CompanyA", "CompanyB"] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_distinct_company_names("Com") + assert len(result) == 2 + + +@pytest.mark.anyio +async def test_get_contact_names_by_company(fuel_code_repo, mock_db): + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = ["John Doe", "Jane Doe"] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_contact_names_by_company("CompanyA", "J") + assert len(result) == 2 + + +@pytest.mark.anyio +async def test_get_contact_email_by_company_and_name(fuel_code_repo, mock_db): + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = ["john@example.com"] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_contact_email_by_company_and_name( + "CompanyA", "John Doe", "john@" + ) + assert len(result) == 1 + + +@pytest.mark.anyio +async def test_get_distinct_fuel_codes_by_code(fuel_code_repo, mock_db): + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = ["101.0", "101.1"] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_distinct_fuel_codes_by_code("101", "BC") + assert len(result) == 2 + + +@pytest.mark.anyio +async def test_get_fuel_code_by_code_prefix(fuel_code_repo, mock_db): + fc = FuelCode(fuel_code_id=10, fuel_suffix="200.0") + mock_result = MagicMock() + mock_result.unique.return_value.scalars.return_value.all.return_value = [fc] + mock_db.execute.return_value = mock_result + + # Mock the next available suffix + fuel_code_repo.get_next_available_sub_version_fuel_code_by_prefix = AsyncMock( + return_value="200.1" + ) + + result = await fuel_code_repo.get_fuel_code_by_code_prefix("200.0", "BC") + assert len(result) == 1 + assert result[0].fuel_suffix == "200.1" + + +@pytest.mark.anyio +async def test_validate_fuel_code(fuel_code_repo, mock_db): + # Mock no existing code + mock_result = MagicMock() + mock_result.scalar_one_or_none.return_value = None + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.validate_fuel_code("300.0", 1) + assert result == "300.0" + + # Mock existing code + mock_result.scalar_one_or_none.return_value = FuelCode( + fuel_code_id=5, fuel_suffix="300.0" + ) + mock_db.execute.return_value = mock_result + fuel_code_repo.get_next_available_sub_version_fuel_code_by_prefix = AsyncMock( + return_value="300.1" + ) + result = await fuel_code_repo.validate_fuel_code("300.0", 1) + assert result == "300.1" + + +@pytest.mark.anyio +async def test_get_next_available_fuel_code_by_prefix(fuel_code_repo, mock_db): + mock_execute_result = MagicMock() + mock_execute_result.scalar_one_or_none.return_value = "102.0" + mock_db.execute.return_value = mock_execute_result + + result = await fuel_code_repo.get_next_available_fuel_code_by_prefix("BC") + assert result == "102.0" + + +@pytest.mark.anyio +async def test_get_next_available_sub_version_fuel_code_by_prefix( + fuel_code_repo, mock_db +): + mock_execute_result = MagicMock() + mock_execute_result.scalar_one_or_none.return_value = "200.1" + mock_db.execute.return_value = mock_execute_result + + result = await fuel_code_repo.get_next_available_sub_version_fuel_code_by_prefix( + "200", 1 + ) + assert result == "200.1" + + +@pytest.mark.anyio +async def test_get_latest_fuel_codes(fuel_code_repo, mock_db, valid_fuel_code): + prefix = FuelCodePrefix(fuel_code_prefix_id=1, prefix="BC") + valid_fuel_code.fuel_code_prefix = prefix + + mock_result = MagicMock() + mock_result.unique.return_value.scalars.return_value.all.return_value = [ + valid_fuel_code + ] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_latest_fuel_codes() + assert len(result) == 1 + # The code increments the version, e.g. "BC101.0" -> "BC101.1" + # Assuming suffix "105.0": + assert result[0]["fuel_code"].endswith(".1") + + +@pytest.mark.anyio +async def test_get_fuel_code_field_options(fuel_code_repo, mock_db): + mock_execute_result = MagicMock() + mock_execute_result.all.return_value = [ + ("CompanyA", "Corn", "USA", None, None, "John Doe", "john@example.com") + ] + mock_db.execute.return_value = mock_execute_result + + result = await fuel_code_repo.get_fuel_code_field_options() + assert len(result) == 1 + + +@pytest.mark.anyio +async def test_get_fp_locations(fuel_code_repo, mock_db): + mock_execute_result = MagicMock() + mock_execute_result.all.return_value = [("CityA", "ProvinceA", "CountryA")] + mock_db.execute.return_value = mock_execute_result + + result = await fuel_code_repo.get_fp_locations() + assert len(result) == 1 + + +@pytest.mark.anyio +async def test_get_fuel_code_by_name(fuel_code_repo, mock_db): + fc = FuelCode(fuel_code_id=50, fuel_suffix="150.0") + mock_result = MagicMock() + mock_result.scalar_one_or_none.return_value = fc + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_fuel_code_by_name("BC150.0") + assert result == fc + + +@pytest.mark.anyio +async def test_get_provision_of_the_act_by_name(fuel_code_repo, mock_db): + poa = ProvisionOfTheAct(provision_of_the_act_id=1, name="Act Name") + mock_result = MagicMock() + mock_result.scalar_one_or_none.return_value = poa + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_provision_of_the_act_by_name("Act Name") + assert result == poa + + +@pytest.mark.anyio +async def test_get_energy_effectiveness_ratio(fuel_code_repo, mock_db): + eer = EnergyEffectivenessRatio(eer_id=1, ratio=1.5) + mock_result = MagicMock() + mock_result.scalars.return_value.first.return_value = eer + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_energy_effectiveness_ratio(1, 2, 3) + assert result == eer + + +@pytest.mark.anyio +async def test_get_target_carbon_intensities(fuel_code_repo, mock_db): + tci = TargetCarbonIntensity( + target_carbon_intensity_id=1, target_carbon_intensity=50.0 + ) + mock_result = MagicMock() + mock_result.scalars.return_value.all.return_value = [tci] + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_target_carbon_intensities(1, "2024") + assert len(result) == 1 + assert result[0] == tci + + +@pytest.mark.anyio +async def test_get_standardized_fuel_data(fuel_code_repo, mock_db): + # Mock dependencies + mock_fuel_type = FuelType( + fuel_type_id=1, fuel_type="Diesel", default_carbon_intensity=80.0 + ) + mock_db.get_one.return_value = mock_fuel_type + mock_db.execute.side_effect = [ + # energy density + MagicMock( + scalars=MagicMock( + return_value=MagicMock( + first=MagicMock(return_value=EnergyDensity(density=35.0)) + ) + ) + ), + # eer + MagicMock( + scalars=MagicMock( + return_value=MagicMock( + first=MagicMock(return_value=EnergyEffectivenessRatio(ratio=2.0)) + ) + ) + ), + # target carbon intensities + MagicMock( + scalars=MagicMock( + return_value=MagicMock( + all=MagicMock( + return_value=[ + TargetCarbonIntensity(target_carbon_intensity=50.0) + ] + ) + ) + ) + ), + # additional carbon intensity + MagicMock( + scalars=MagicMock( + return_value=MagicMock( + one_or_none=MagicMock( + return_value=AdditionalCarbonIntensity(intensity=5.0) + ) + ) + ) + ), + ] + + result = await fuel_code_repo.get_standardized_fuel_data( + fuel_type_id=1, fuel_category_id=2, end_use_id=3, compliance_period="2024" + ) + assert result.effective_carbon_intensity == 80.0 + assert result.target_ci == 50.0 + assert result.eer == 2.0 + assert result.energy_density == 35.0 + assert result.uci == 5.0 + + +@pytest.mark.anyio +async def test_get_additional_carbon_intensity(fuel_code_repo, mock_db): + aci = AdditionalCarbonIntensity(additional_uci_id=1, intensity=10.0) + mock_result = MagicMock() + mock_result.scalars.return_value.one_or_none.return_value = aci + mock_db.execute.return_value = mock_result + + result = await fuel_code_repo.get_additional_carbon_intensity(1, 2) + assert result == aci diff --git a/backend/lcfs/web/api/fuel_code/repo.py b/backend/lcfs/web/api/fuel_code/repo.py index 4cc699008..594bd156f 100644 --- a/backend/lcfs/web/api/fuel_code/repo.py +++ b/backend/lcfs/web/api/fuel_code/repo.py @@ -515,7 +515,7 @@ async def get_distinct_fuel_codes_by_code( @repo_handler async def get_fuel_code_by_code_prefix( self, fuel_suffix: str, prefix: str - ) -> List[str]: + ) -> list[FuelCodeCloneSchema]: query = ( select(FuelCode) .options( @@ -751,7 +751,7 @@ async def get_fuel_code_by_name(self, fuel_code: str) -> FuelCode: .options( contains_eager(FuelCode.fuel_code_prefix), joinedload(FuelCode.fuel_code_status), - joinedload(FuelCode.fuel_code_type), + joinedload(FuelCode.fuel_type), ) .where( and_( From 8b438ffcbb2b1522e37d11ddb45edcc32379b50d Mon Sep 17 00:00:00 2001 From: Arturo Reyes Lopez Date: Thu, 5 Dec 2024 13:40:55 -0700 Subject: [PATCH 17/25] Add organization field to FSE --- .../versions/2024-12-05-22-59_9206124a098b.py | 25 ++++++++++++ .../models/compliance/FinalSupplyEquipment.py | 1 + .../lcfs/web/api/compliance_report/schema.py | 5 +-- .../web/api/final_supply_equipment/repo.py | 39 +++++++++++++++++-- .../web/api/final_supply_equipment/schema.py | 2 + .../api/final_supply_equipment/services.py | 8 +++- .../locales/en/finalSupplyEquipment.json | 1 + .../FinalSupplyEquipmentSummary.jsx | 6 +++ .../views/FinalSupplyEquipments/_schema.jsx | 38 +++++++++++++++++- 9 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 backend/lcfs/db/migrations/versions/2024-12-05-22-59_9206124a098b.py diff --git a/backend/lcfs/db/migrations/versions/2024-12-05-22-59_9206124a098b.py b/backend/lcfs/db/migrations/versions/2024-12-05-22-59_9206124a098b.py new file mode 100644 index 000000000..a32a83434 --- /dev/null +++ b/backend/lcfs/db/migrations/versions/2024-12-05-22-59_9206124a098b.py @@ -0,0 +1,25 @@ +"""Add Organization to FSE + +Revision ID: 9206124a098b +Revises: aeaa26f5cdd5 +Create Date: 2024-12-04 09:59:22.876386 + +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = '9206124a098b' +down_revision = '8491890dd688' +branch_labels = None +depends_on = None + + +def upgrade(): + # Add the column 'organization' to 'final_supply_equipment' table + op.add_column("final_supply_equipment", sa.Column("organization", sa.String(), nullable=True)) + + +def downgrade(): + # Remove the column 'organization' from 'final_supply_equipment' table + op.drop_column("final_supply_equipment", "organization") \ No newline at end of file diff --git a/backend/lcfs/db/models/compliance/FinalSupplyEquipment.py b/backend/lcfs/db/models/compliance/FinalSupplyEquipment.py index e3b8d5685..263f38914 100644 --- a/backend/lcfs/db/models/compliance/FinalSupplyEquipment.py +++ b/backend/lcfs/db/models/compliance/FinalSupplyEquipment.py @@ -123,6 +123,7 @@ class FinalSupplyEquipment(BaseModel, Auditable): Double, nullable=False, comment="The longitude of the equipment location." ) notes = Column(Text, comment="Any additional notes related to the equipment.") + organization = Column(Text, comment="External organization.") # relationships compliance_report = relationship( diff --git a/backend/lcfs/web/api/compliance_report/schema.py b/backend/lcfs/web/api/compliance_report/schema.py index 0f157be8b..5d6f1ec1e 100644 --- a/backend/lcfs/web/api/compliance_report/schema.py +++ b/backend/lcfs/web/api/compliance_report/schema.py @@ -40,10 +40,6 @@ class CompliancePeriodSchema(BaseSchema): display_order: Optional[int] = None -class ComplianceReportOrganizationSchema(BaseSchema): - organization_id: int - name: str - class SummarySchema(BaseSchema): summary_id: int @@ -118,6 +114,7 @@ class FSEOptionsSchema(BaseSchema): class FinalSupplyEquipmentSchema(BaseSchema): final_supply_equipment_id: int compliance_report_id: int + organization: str supply_from_date: date supply_to_date: date registration_nbr: str diff --git a/backend/lcfs/web/api/final_supply_equipment/repo.py b/backend/lcfs/web/api/final_supply_equipment/repo.py index 398f01585..55763c0aa 100644 --- a/backend/lcfs/web/api/final_supply_equipment/repo.py +++ b/backend/lcfs/web/api/final_supply_equipment/repo.py @@ -1,6 +1,6 @@ import structlog from typing import List, Tuple -from lcfs.db.models.compliance import EndUserType, FinalSupplyEquipment +from lcfs.db.models.compliance import EndUserType, FinalSupplyEquipment, ComplianceReport from lcfs.db.models.compliance.FinalSupplyEquipmentRegNumber import ( FinalSupplyEquipmentRegNumber, ) @@ -27,8 +27,14 @@ def __init__(self, db: AsyncSession = Depends(get_async_db_session)): @repo_handler async def get_fse_options( - self, - ) -> Tuple[List[EndUseType], List[LevelOfEquipment], List[FuelMeasurementType], List[PortsEnum]]: + self, organization + ) -> Tuple[ + List[EndUseType], + List[LevelOfEquipment], + List[FuelMeasurementType], + List[PortsEnum], + List[str], + ]: """ Retrieve all FSE options in a single database transaction """ @@ -37,13 +43,15 @@ async def get_fse_options( levels_of_equipment = await self.get_levels_of_equipment() fuel_measurement_types = await self.get_fuel_measurement_types() intended_user_types = await self.get_intended_user_types() + organizations = await self.get_organizations(organization) ports = list(PortsEnum) return ( intended_use_types, levels_of_equipment, fuel_measurement_types, intended_user_types, - ports + ports, + organizations, ) async def get_intended_use_types(self) -> List[EndUseType]: @@ -94,6 +102,29 @@ async def get_intended_user_types(self) -> List[EndUserType]: .all() ) + async def get_organizations(self, organization) -> List[str]: + """ + Retrieve unique organization names for Final Supply Equipment records + associated with the given organization_id via ComplianceReport. + + Args: + organization_id (int): The ID of the organization. + + Returns: + List[str]: A list of unique organization names. + """ + organization_names = ( + await self.db.execute( + select(distinct(FinalSupplyEquipment.organization)) + .join(ComplianceReport, FinalSupplyEquipment.compliance_report_id == ComplianceReport.compliance_report_id) + .filter(ComplianceReport.organization_id == organization.organization_id) + .filter(FinalSupplyEquipment.organization.isnot(None)) + ) + ).all() + + # Extract strings from the list of tuples + return [name[0] for name in organization_names] + @repo_handler async def get_intended_user_by_name(self, intended_user: str) -> EndUseType: """ diff --git a/backend/lcfs/web/api/final_supply_equipment/schema.py b/backend/lcfs/web/api/final_supply_equipment/schema.py index 38f80b2fa..fbe7ed468 100644 --- a/backend/lcfs/web/api/final_supply_equipment/schema.py +++ b/backend/lcfs/web/api/final_supply_equipment/schema.py @@ -33,11 +33,13 @@ class FSEOptionsSchema(BaseSchema): levels_of_equipment: List[LevelOfEquipmentSchema] intended_user_types: List[EndUserTypeSchema] ports: List[PortsEnum] + organizations: List[str] class FinalSupplyEquipmentCreateSchema(BaseSchema): final_supply_equipment_id: Optional[int] = None compliance_report_id: Optional[int] = None + organization: str supply_from_date: date supply_to_date: date kwh_usage: float diff --git a/backend/lcfs/web/api/final_supply_equipment/services.py b/backend/lcfs/web/api/final_supply_equipment/services.py index 9cc225935..c2d1fb5e8 100644 --- a/backend/lcfs/web/api/final_supply_equipment/services.py +++ b/backend/lcfs/web/api/final_supply_equipment/services.py @@ -29,13 +29,15 @@ def __init__( @service_handler async def get_fse_options(self): """Fetches all FSE options concurrently.""" + organization= self.request.user.organization ( intended_use_types, levels_of_equipment, fuel_measurement_types, intended_user_types, - ports - ) = await self.repo.get_fse_options() + ports, + organizations + ) = await self.repo.get_fse_options(organization) return { "intended_use_types": [ @@ -52,6 +54,7 @@ async def get_fse_options(self): EndUserTypeSchema.model_validate(u) for u in intended_user_types ], "ports": [port.value for port in ports], + "organizations": organizations, } async def convert_to_fse_model(self, fse: FinalSupplyEquipmentCreateSchema): @@ -141,6 +144,7 @@ async def update_final_supply_equipment( if not existing_fse: raise ValueError("final supply equipment not found") + existing_fse.organization = fse_data.organization existing_fse.kwh_usage = fse_data.kwh_usage existing_fse.serial_nbr = fse_data.serial_nbr existing_fse.manufacturer = fse_data.manufacturer diff --git a/frontend/src/assets/locales/en/finalSupplyEquipment.json b/frontend/src/assets/locales/en/finalSupplyEquipment.json index 00e74933f..b8449d574 100644 --- a/frontend/src/assets/locales/en/finalSupplyEquipment.json +++ b/frontend/src/assets/locales/en/finalSupplyEquipment.json @@ -27,6 +27,7 @@ "rows": "rows", "finalSupplyEquipmentColLabels": { "complianceReportId": "Compliance Report ID", + "organization": "Organization", "supplyFrom": "Supply date range", "kwhUsage":"kWh usage", "supplyFromDate": "Dates of supply from", diff --git a/frontend/src/views/FinalSupplyEquipments/FinalSupplyEquipmentSummary.jsx b/frontend/src/views/FinalSupplyEquipments/FinalSupplyEquipmentSummary.jsx index 84437a91b..81ce8415d 100644 --- a/frontend/src/views/FinalSupplyEquipments/FinalSupplyEquipmentSummary.jsx +++ b/frontend/src/views/FinalSupplyEquipments/FinalSupplyEquipmentSummary.jsx @@ -48,6 +48,12 @@ export const FinalSupplyEquipmentSummary = ({ data }) => { ) const columns = useMemo( () => [ + { + headerName: t( + 'finalSupplyEquipment:finalSupplyEquipmentColLabels.organization' + ), + field: 'organization' + }, { headerName: t( 'finalSupplyEquipment:finalSupplyEquipmentColLabels.supplyFromDate' diff --git a/frontend/src/views/FinalSupplyEquipments/_schema.jsx b/frontend/src/views/FinalSupplyEquipments/_schema.jsx index 2974a9725..244eaa112 100644 --- a/frontend/src/views/FinalSupplyEquipments/_schema.jsx +++ b/frontend/src/views/FinalSupplyEquipments/_schema.jsx @@ -12,7 +12,7 @@ import i18n from '@/i18n' import { actions, validation } from '@/components/BCDataGrid/columns' import moment from 'moment' import { CommonArrayRenderer } from '@/utils/grid/cellRenderers' -import { StandardCellErrors } from '@/utils/grid/errorRenderers' +import { StandardCellWarningAndErrors, StandardCellErrors } from '@/utils/grid/errorRenderers' import { apiRoutes } from '@/constants/routes' import { numberFormatter } from '@/utils/formatters.js' @@ -41,6 +41,42 @@ export const finalSupplyEquipmentColDefs = ( cellDataType: 'text', hide: true }, + { + field: 'organization', + headerComponent: RequiredHeader, + headerName: i18n.t( + 'finalSupplyEquipment:finalSupplyEquipmentColLabels.organization' + ), + cellEditor: AutocompleteCellEditor, + cellRenderer: (params) => + params.value || + (!params.value && Select), + cellEditorParams: { + options: optionsData?.organizations?.sort() || [], + multiple: false, + disableCloseOnSelect: false, + freeSolo: true, + openOnFocus: true, + }, + cellStyle: (params) => + StandardCellWarningAndErrors(params, errors), + suppressKeyboardEvent, + minWidth: 260, + editable: true, + valueGetter: (params) => { + return params.data?.organization || ''; + }, + valueSetter: (params) => { + if (params.newValue) { + const isValidOrganization = optionsData?.organizations.includes(params.newValue); + + params.data.organization = isValidOrganization ? params.newValue : params.newValue; + return true; + } + return false; + }, + tooltipValueGetter: (params) => "Select the organization from the list" + }, { field: 'supplyFrom', headerName: i18n.t( From 81857ea7f1476c5636f7df81432654ddeecd42e5 Mon Sep 17 00:00:00 2001 From: Arturo Reyes Lopez Date: Mon, 9 Dec 2024 10:02:06 -0700 Subject: [PATCH 18/25] rename organization for organization_name --- ...24a098b.py => 2024-12-06-09-59_9206124a098b.py} | 12 ++++++------ .../db/models/compliance/FinalSupplyEquipment.py | 2 +- backend/lcfs/web/api/compliance_report/schema.py | 2 +- .../lcfs/web/api/final_supply_equipment/repo.py | 10 +++++----- .../lcfs/web/api/final_supply_equipment/schema.py | 4 ++-- .../web/api/final_supply_equipment/services.py | 8 ++++---- .../assets/locales/en/finalSupplyEquipment.json | 2 +- .../FinalSupplyEquipmentSummary.jsx | 4 ++-- .../src/views/FinalSupplyEquipments/_schema.jsx | 14 +++++++------- 9 files changed, 29 insertions(+), 29 deletions(-) rename backend/lcfs/db/migrations/versions/{2024-12-05-22-59_9206124a098b.py => 2024-12-06-09-59_9206124a098b.py} (53%) diff --git a/backend/lcfs/db/migrations/versions/2024-12-05-22-59_9206124a098b.py b/backend/lcfs/db/migrations/versions/2024-12-06-09-59_9206124a098b.py similarity index 53% rename from backend/lcfs/db/migrations/versions/2024-12-05-22-59_9206124a098b.py rename to backend/lcfs/db/migrations/versions/2024-12-06-09-59_9206124a098b.py index a32a83434..fc805ff14 100644 --- a/backend/lcfs/db/migrations/versions/2024-12-05-22-59_9206124a098b.py +++ b/backend/lcfs/db/migrations/versions/2024-12-06-09-59_9206124a098b.py @@ -1,4 +1,4 @@ -"""Add Organization to FSE +"""Add Organization name to FSE Revision ID: 9206124a098b Revises: aeaa26f5cdd5 @@ -10,16 +10,16 @@ # revision identifiers, used by Alembic. revision = '9206124a098b' -down_revision = '8491890dd688' +down_revision = '26ab15f8ab18' branch_labels = None depends_on = None def upgrade(): - # Add the column 'organization' to 'final_supply_equipment' table - op.add_column("final_supply_equipment", sa.Column("organization", sa.String(), nullable=True)) + # Add the column 'organization_name' to 'final_supply_equipment' table + op.add_column("final_supply_equipment", sa.Column("organization_name", sa.String(), nullable=True)) def downgrade(): - # Remove the column 'organization' from 'final_supply_equipment' table - op.drop_column("final_supply_equipment", "organization") \ No newline at end of file + # Remove the column 'organization_name' from 'final_supply_equipment' table + op.drop_column("final_supply_equipment", "organization_name") \ No newline at end of file diff --git a/backend/lcfs/db/models/compliance/FinalSupplyEquipment.py b/backend/lcfs/db/models/compliance/FinalSupplyEquipment.py index 263f38914..90bd37b27 100644 --- a/backend/lcfs/db/models/compliance/FinalSupplyEquipment.py +++ b/backend/lcfs/db/models/compliance/FinalSupplyEquipment.py @@ -123,7 +123,7 @@ class FinalSupplyEquipment(BaseModel, Auditable): Double, nullable=False, comment="The longitude of the equipment location." ) notes = Column(Text, comment="Any additional notes related to the equipment.") - organization = Column(Text, comment="External organization.") + organization_name = Column(Text, comment="External organization name.") # relationships compliance_report = relationship( diff --git a/backend/lcfs/web/api/compliance_report/schema.py b/backend/lcfs/web/api/compliance_report/schema.py index 5d6f1ec1e..9eb215c53 100644 --- a/backend/lcfs/web/api/compliance_report/schema.py +++ b/backend/lcfs/web/api/compliance_report/schema.py @@ -114,7 +114,7 @@ class FSEOptionsSchema(BaseSchema): class FinalSupplyEquipmentSchema(BaseSchema): final_supply_equipment_id: int compliance_report_id: int - organization: str + organization_name: str supply_from_date: date supply_to_date: date registration_nbr: str diff --git a/backend/lcfs/web/api/final_supply_equipment/repo.py b/backend/lcfs/web/api/final_supply_equipment/repo.py index 55763c0aa..b3680584f 100644 --- a/backend/lcfs/web/api/final_supply_equipment/repo.py +++ b/backend/lcfs/web/api/final_supply_equipment/repo.py @@ -43,7 +43,7 @@ async def get_fse_options( levels_of_equipment = await self.get_levels_of_equipment() fuel_measurement_types = await self.get_fuel_measurement_types() intended_user_types = await self.get_intended_user_types() - organizations = await self.get_organizations(organization) + organization_names = await self.get_organization_names(organization) ports = list(PortsEnum) return ( intended_use_types, @@ -51,7 +51,7 @@ async def get_fse_options( fuel_measurement_types, intended_user_types, ports, - organizations, + organization_names, ) async def get_intended_use_types(self) -> List[EndUseType]: @@ -102,7 +102,7 @@ async def get_intended_user_types(self) -> List[EndUserType]: .all() ) - async def get_organizations(self, organization) -> List[str]: + async def get_organization_names(self, organization) -> List[str]: """ Retrieve unique organization names for Final Supply Equipment records associated with the given organization_id via ComplianceReport. @@ -115,10 +115,10 @@ async def get_organizations(self, organization) -> List[str]: """ organization_names = ( await self.db.execute( - select(distinct(FinalSupplyEquipment.organization)) + select(distinct(FinalSupplyEquipment.organization_name)) .join(ComplianceReport, FinalSupplyEquipment.compliance_report_id == ComplianceReport.compliance_report_id) .filter(ComplianceReport.organization_id == organization.organization_id) - .filter(FinalSupplyEquipment.organization.isnot(None)) + .filter(FinalSupplyEquipment.organization_name.isnot(None)) ) ).all() diff --git a/backend/lcfs/web/api/final_supply_equipment/schema.py b/backend/lcfs/web/api/final_supply_equipment/schema.py index fbe7ed468..2dc81e8f3 100644 --- a/backend/lcfs/web/api/final_supply_equipment/schema.py +++ b/backend/lcfs/web/api/final_supply_equipment/schema.py @@ -33,13 +33,13 @@ class FSEOptionsSchema(BaseSchema): levels_of_equipment: List[LevelOfEquipmentSchema] intended_user_types: List[EndUserTypeSchema] ports: List[PortsEnum] - organizations: List[str] + organization_names: List[str] class FinalSupplyEquipmentCreateSchema(BaseSchema): final_supply_equipment_id: Optional[int] = None compliance_report_id: Optional[int] = None - organization: str + organization_name: str supply_from_date: date supply_to_date: date kwh_usage: float diff --git a/backend/lcfs/web/api/final_supply_equipment/services.py b/backend/lcfs/web/api/final_supply_equipment/services.py index c2d1fb5e8..a70b1ce4b 100644 --- a/backend/lcfs/web/api/final_supply_equipment/services.py +++ b/backend/lcfs/web/api/final_supply_equipment/services.py @@ -29,14 +29,14 @@ def __init__( @service_handler async def get_fse_options(self): """Fetches all FSE options concurrently.""" - organization= self.request.user.organization + organization = self.request.user.organization ( intended_use_types, levels_of_equipment, fuel_measurement_types, intended_user_types, ports, - organizations + organization_names, ) = await self.repo.get_fse_options(organization) return { @@ -54,7 +54,7 @@ async def get_fse_options(self): EndUserTypeSchema.model_validate(u) for u in intended_user_types ], "ports": [port.value for port in ports], - "organizations": organizations, + "organization_names": organization_names, } async def convert_to_fse_model(self, fse: FinalSupplyEquipmentCreateSchema): @@ -144,7 +144,7 @@ async def update_final_supply_equipment( if not existing_fse: raise ValueError("final supply equipment not found") - existing_fse.organization = fse_data.organization + existing_fse.organization_name = fse_data.organization_name existing_fse.kwh_usage = fse_data.kwh_usage existing_fse.serial_nbr = fse_data.serial_nbr existing_fse.manufacturer = fse_data.manufacturer diff --git a/frontend/src/assets/locales/en/finalSupplyEquipment.json b/frontend/src/assets/locales/en/finalSupplyEquipment.json index b8449d574..d48862f59 100644 --- a/frontend/src/assets/locales/en/finalSupplyEquipment.json +++ b/frontend/src/assets/locales/en/finalSupplyEquipment.json @@ -27,7 +27,7 @@ "rows": "rows", "finalSupplyEquipmentColLabels": { "complianceReportId": "Compliance Report ID", - "organization": "Organization", + "organizationName": "Organization", "supplyFrom": "Supply date range", "kwhUsage":"kWh usage", "supplyFromDate": "Dates of supply from", diff --git a/frontend/src/views/FinalSupplyEquipments/FinalSupplyEquipmentSummary.jsx b/frontend/src/views/FinalSupplyEquipments/FinalSupplyEquipmentSummary.jsx index 81ce8415d..1a2f7851e 100644 --- a/frontend/src/views/FinalSupplyEquipments/FinalSupplyEquipmentSummary.jsx +++ b/frontend/src/views/FinalSupplyEquipments/FinalSupplyEquipmentSummary.jsx @@ -50,9 +50,9 @@ export const FinalSupplyEquipmentSummary = ({ data }) => { () => [ { headerName: t( - 'finalSupplyEquipment:finalSupplyEquipmentColLabels.organization' + 'finalSupplyEquipment:finalSupplyEquipmentColLabels.organizationName' ), - field: 'organization' + field: 'organizationName' }, { headerName: t( diff --git a/frontend/src/views/FinalSupplyEquipments/_schema.jsx b/frontend/src/views/FinalSupplyEquipments/_schema.jsx index 244eaa112..b00d5f437 100644 --- a/frontend/src/views/FinalSupplyEquipments/_schema.jsx +++ b/frontend/src/views/FinalSupplyEquipments/_schema.jsx @@ -42,17 +42,17 @@ export const finalSupplyEquipmentColDefs = ( hide: true }, { - field: 'organization', + field: 'organizationName', headerComponent: RequiredHeader, headerName: i18n.t( - 'finalSupplyEquipment:finalSupplyEquipmentColLabels.organization' + 'finalSupplyEquipment:finalSupplyEquipmentColLabels.organizationName' ), cellEditor: AutocompleteCellEditor, cellRenderer: (params) => params.value || (!params.value && Select), cellEditorParams: { - options: optionsData?.organizations?.sort() || [], + options: optionsData?.organizationNames?.sort() || [], multiple: false, disableCloseOnSelect: false, freeSolo: true, @@ -64,18 +64,18 @@ export const finalSupplyEquipmentColDefs = ( minWidth: 260, editable: true, valueGetter: (params) => { - return params.data?.organization || ''; + return params.data?.organizationName || ''; }, valueSetter: (params) => { if (params.newValue) { - const isValidOrganization = optionsData?.organizations.includes(params.newValue); + const isValidOrganizationName = optionsData?.organizationNames.includes(params.newValue); - params.data.organization = isValidOrganization ? params.newValue : params.newValue; + params.data.organizationName = isValidOrganizationName ? params.newValue : params.newValue; return true; } return false; }, - tooltipValueGetter: (params) => "Select the organization from the list" + tooltipValueGetter: (params) => "Select the organization name from the list" }, { field: 'supplyFrom', From 04f1b780b597c1d8964d3bfa36aa702bb4d9d390 Mon Sep 17 00:00:00 2001 From: Arturo Reyes Lopez Date: Wed, 11 Dec 2024 12:12:19 -0700 Subject: [PATCH 19/25] fixing migration issues --- .../db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py b/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py index d812b2a94..8ec2b8223 100644 --- a/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py +++ b/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py @@ -11,7 +11,7 @@ # revision identifiers, used by Alembic. revision = "cd8698fe40e6" -down_revision = "26ab15f8ab18" +down_revision = "9206124a098b" branch_labels = None depends_on = None From 7450fd1b49d06f920ad4f4a132285cb5d96bab93 Mon Sep 17 00:00:00 2001 From: Arturo Reyes Lopez Date: Mon, 9 Dec 2024 14:22:36 -0700 Subject: [PATCH 20/25] Update fuel type for other_uses --- .../versions/2024-12-09-19-31_7ae38a8413ab.py | 95 +++++++++++++++++++ backend/lcfs/web/api/other_uses/repo.py | 90 ++++++++++++++++-- 2 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 backend/lcfs/db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py diff --git a/backend/lcfs/db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py b/backend/lcfs/db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py new file mode 100644 index 000000000..da29c2fc1 --- /dev/null +++ b/backend/lcfs/db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py @@ -0,0 +1,95 @@ +"""Update Fuel Types measured in volume to be other-uses + +Revision ID: 7ae38a8413ab +Revises: 26ab15f8ab18 +Create Date: 2024-12-09 19:31:18.199089 + +""" + +import sqlalchemy as sa +from alembic import op +from datetime import datetime + +# revision identifiers, used by Alembic. +revision = "7ae38a8413ab" +down_revision = "26ab15f8ab18" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + current_time = datetime.now() + + # Update the `other_uses_fossil_derived` field for all specified fuel types + op.execute( + f""" + UPDATE fuel_type + SET other_uses_fossil_derived = true, + update_date = '{current_time}', + update_user = 'no_user' + WHERE fuel_type IN ( + 'Alternative jet fuel', + 'Biodiesel', + 'Ethanol', + 'HDRD', + 'Renewable gasoline', + 'Renewable naphtha' + ) + """ + ) + + # Update the `other_uses_fossil_derived` field for all specified fuel types + op.execute( + f""" + UPDATE fuel_type + SET other_uses_fossil_derived = false, + update_date = '{current_time}', + update_user = 'no_user' + WHERE fuel_type IN ( + 'CNG', + 'Electricity', + 'Hydrogen', + 'LNG', + 'Propane' + ) + """ + ) + + +def downgrade() -> None: + current_time = datetime.now() + + # Revert the `other_uses_fossil_derived` field to false for the first set of fuel types + op.execute( + f""" + UPDATE fuel_type + SET other_uses_fossil_derived = false, + update_date = '{current_time}', + update_user = 'no_user' + WHERE fuel_type IN ( + 'Alternative jet fuel', + 'Biodiesel', + 'Ethanol', + 'HDRD', + 'Renewable gasoline', + 'Renewable naphtha' + ) + """ + ) + + # Revert the `other_uses_fossil_derived` field to true for the second set of fuel types + op.execute( + f""" + UPDATE fuel_type + SET other_uses_fossil_derived = true, + update_date = '{current_time}', + update_user = 'no_user' + WHERE fuel_type IN ( + 'CNG', + 'Electricity', + 'Hydrogen', + 'LNG', + 'Propane' + ) + """ + ) diff --git a/backend/lcfs/web/api/other_uses/repo.py b/backend/lcfs/web/api/other_uses/repo.py index 68a8a434a..8e0f5ca4d 100644 --- a/backend/lcfs/web/api/other_uses/repo.py +++ b/backend/lcfs/web/api/other_uses/repo.py @@ -1,20 +1,21 @@ import structlog -from typing import List, Optional, Tuple, Any +from datetime import date +from typing import List, Optional, Tuple, Dict, Any from fastapi import Depends - from lcfs.db.base import ActionTypeEnum, UserTypeEnum from lcfs.db.dependencies import get_async_db_session -from sqlalchemy import select, delete, func, case, and_ -from sqlalchemy.orm import joinedload +from sqlalchemy import select, delete, func, case, and_, or_ +from sqlalchemy.orm import joinedload, contains_eager from sqlalchemy.ext.asyncio import AsyncSession from lcfs.db.models.compliance import ComplianceReport from lcfs.db.models.compliance.OtherUses import OtherUses from lcfs.db.models.fuel.ProvisionOfTheAct import ProvisionOfTheAct from lcfs.db.models.fuel.FuelCode import FuelCode -from lcfs.db.models.fuel.FuelType import QuantityUnitsEnum +from lcfs.db.models.fuel.FuelType import FuelType, QuantityUnitsEnum +from lcfs.db.models.fuel.FuelInstance import FuelInstance from lcfs.web.api.fuel_code.repo import FuelCodeRepository from lcfs.web.api.other_uses.schema import OtherUsesSchema from lcfs.web.api.base import PaginationRequestSchema @@ -37,7 +38,7 @@ def __init__( async def get_table_options(self) -> dict: """Get all table options""" fuel_categories = await self.fuel_code_repo.get_fuel_categories() - fuel_types = await self.fuel_code_repo.get_formatted_fuel_types() + fuel_types = await self.get_formatted_fuel_types() expected_uses = await self.fuel_code_repo.get_expected_use_types() units_of_measure = [unit.value for unit in QuantityUnitsEnum] provisions_of_the_act = ( @@ -302,3 +303,80 @@ async def get_other_use_version_by_user( result = await self.db.execute(query) return result.scalars().first() + + @repo_handler + async def get_formatted_fuel_types(self) -> List[Dict[str, Any]]: + """Get all fuel type options with their associated fuel categories and fuel codes for other uses""" + # Define the filtering conditions for fuel codes + current_date = date.today() + fuel_code_filters = ( + or_(FuelCode.effective_date == None, FuelCode.effective_date <= current_date) + & or_(FuelCode.expiration_date == None, FuelCode.expiration_date > current_date) + & (FuelType.other_uses_fossil_derived == True) + ) + + # Build the query with filtered fuel_codes + query = ( + select(FuelType) + .outerjoin(FuelType.fuel_instances) + .outerjoin(FuelInstance.fuel_category) + .outerjoin(FuelType.fuel_codes) + .where(fuel_code_filters) + .options( + contains_eager(FuelType.fuel_instances).contains_eager( + FuelInstance.fuel_category + ), + contains_eager(FuelType.fuel_codes), + joinedload(FuelType.provision_1), + joinedload(FuelType.provision_2), + ) + ) + + result = await self.db.execute(query) + fuel_types = result.unique().scalars().all() + + # Prepare the data in the format matching your schema + formatted_fuel_types = [] + for fuel_type in fuel_types: + formatted_fuel_type = { + "fuel_type_id": fuel_type.fuel_type_id, + "fuel_type": fuel_type.fuel_type, + "default_carbon_intensity": fuel_type.default_carbon_intensity, + "units": fuel_type.units if fuel_type.units else None, + "unrecognized": fuel_type.unrecognized, + "fuel_categories": [ + { + "fuel_category_id": fc.fuel_category.fuel_category_id, + "category": fc.fuel_category.category, + } + for fc in fuel_type.fuel_instances + ], + "fuel_codes": [ + { + "fuel_code_id": fc.fuel_code_id, + "fuel_code": fc.fuel_code, + "carbon_intensity": fc.carbon_intensity, + } + for fc in fuel_type.fuel_codes + ], + "provision_of_the_act": [], + } + + if fuel_type.provision_1: + formatted_fuel_type["provision_of_the_act"].append( + { + "provision_of_the_act_id": fuel_type.provision_1_id, + "name": fuel_type.provision_1.name, + } + ) + + if fuel_type.provision_2: + formatted_fuel_type["provision_of_the_act"].append( + { + "provision_of_the_act_id": fuel_type.provision_2_id, + "name": fuel_type.provision_2.name, + } + ) + formatted_fuel_types.append(formatted_fuel_type) + + return formatted_fuel_types \ No newline at end of file From 4573d8f5bfc4760526b7f5db2be9d81410a9ca03 Mon Sep 17 00:00:00 2001 From: Arturo Reyes Lopez Date: Mon, 9 Dec 2024 16:32:10 -0700 Subject: [PATCH 21/25] updating pytest --- .../tests/other_uses/test_other_uses_repo.py | 27 ++++++++++++++----- backend/lcfs/web/api/other_uses/repo.py | 6 ++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/backend/lcfs/tests/other_uses/test_other_uses_repo.py b/backend/lcfs/tests/other_uses/test_other_uses_repo.py index 8bd39f562..97b5308f5 100644 --- a/backend/lcfs/tests/other_uses/test_other_uses_repo.py +++ b/backend/lcfs/tests/other_uses/test_other_uses_repo.py @@ -11,14 +11,19 @@ @pytest.fixture -def mock_db_session(): +def mock_query_result(): + # Setup mock for database query result chain + mock_result = AsyncMock() + mock_result.unique = MagicMock(return_value=mock_result) + mock_result.scalars = MagicMock(return_value=mock_result) + mock_result.all = MagicMock(return_value=[MagicMock(spec=OtherUses)]) + return mock_result + + +@pytest.fixture +def mock_db_session(mock_query_result): session = MagicMock(spec=AsyncSession) - execute_result = AsyncMock() - execute_result.unique = MagicMock(return_value=execute_result) - execute_result.scalars = MagicMock(return_value=execute_result) - execute_result.all = MagicMock(return_value=[MagicMock(spec=OtherUses)]) - execute_result.first = MagicMock(return_value=MagicMock(spec=OtherUses)) - session.execute.return_value = execute_result + session.execute = AsyncMock(return_value=mock_query_result) return session @@ -29,6 +34,14 @@ def other_uses_repo(mock_db_session): repo.fuel_code_repo.get_fuel_categories = AsyncMock(return_value=[]) repo.fuel_code_repo.get_fuel_types = AsyncMock(return_value=[]) repo.fuel_code_repo.get_expected_use_types = AsyncMock(return_value=[]) + + # Mock for local get_formatted_fuel_types method + async def mock_get_formatted_fuel_types(): + mock_result = await mock_db_session.execute(AsyncMock()) + return mock_result.unique().scalars().all() + + repo.get_formatted_fuel_types = AsyncMock(side_effect=mock_get_formatted_fuel_types) + return repo diff --git a/backend/lcfs/web/api/other_uses/repo.py b/backend/lcfs/web/api/other_uses/repo.py index 8e0f5ca4d..595de5882 100644 --- a/backend/lcfs/web/api/other_uses/repo.py +++ b/backend/lcfs/web/api/other_uses/repo.py @@ -76,7 +76,7 @@ async def get_latest_other_uses_by_group_uuid( ) result = await self.db.execute(query) - return result.scalars().first() + return await result.unique().scalars().first() @repo_handler async def get_other_uses(self, compliance_report_id: int) -> List[OtherUsesSchema]: @@ -302,7 +302,7 @@ async def get_other_use_version_by_user( ) result = await self.db.execute(query) - return result.scalars().first() + return await result.scalars().first() @repo_handler async def get_formatted_fuel_types(self) -> List[Dict[str, Any]]: @@ -333,7 +333,7 @@ async def get_formatted_fuel_types(self) -> List[Dict[str, Any]]: ) result = await self.db.execute(query) - fuel_types = result.unique().scalars().all() + fuel_types = await result.unique().scalars().all() # Prepare the data in the format matching your schema formatted_fuel_types = [] From 8580d716d28eec96ef590414581998e82b2e9c12 Mon Sep 17 00:00:00 2001 From: Arturo Reyes Lopez Date: Wed, 11 Dec 2024 10:26:34 -0700 Subject: [PATCH 22/25] changes in await --- .../tests/other_uses/test_other_uses_repo.py | 19 +++++++++---------- backend/lcfs/web/api/other_uses/repo.py | 15 ++++++++++----- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/backend/lcfs/tests/other_uses/test_other_uses_repo.py b/backend/lcfs/tests/other_uses/test_other_uses_repo.py index 97b5308f5..8ef79cd70 100644 --- a/backend/lcfs/tests/other_uses/test_other_uses_repo.py +++ b/backend/lcfs/tests/other_uses/test_other_uses_repo.py @@ -207,22 +207,21 @@ async def test_get_latest_other_uses_by_group_uuid(other_uses_repo, mock_db_sess mock_other_use_gov.user_type = UserTypeEnum.GOVERNMENT mock_other_use_gov.version = 2 - mock_other_use_supplier = MagicMock(spec=OtherUses) - mock_other_use_supplier.user_type = UserTypeEnum.SUPPLIER - mock_other_use_supplier.version = 3 - - # Mock response with both government and supplier versions - mock_db_session.execute.return_value.scalars.return_value.first.side_effect = [ - mock_other_use_gov, - mock_other_use_supplier, - ] + # Setup mock result chain + mock_result = AsyncMock() + mock_result.unique = MagicMock(return_value=mock_result) + mock_result.scalars = MagicMock(return_value=mock_result) + mock_result.first = MagicMock(return_value=mock_other_use_gov) + + # Configure mock db session + mock_db_session.execute = AsyncMock(return_value=mock_result) + other_uses_repo.db = mock_db_session result = await other_uses_repo.get_latest_other_uses_by_group_uuid(group_uuid) assert result.user_type == UserTypeEnum.GOVERNMENT assert result.version == 2 - @pytest.mark.anyio async def test_get_other_use_version_by_user(other_uses_repo, mock_db_session): group_uuid = "test-group-uuid" diff --git a/backend/lcfs/web/api/other_uses/repo.py b/backend/lcfs/web/api/other_uses/repo.py index 595de5882..9f49a3d5d 100644 --- a/backend/lcfs/web/api/other_uses/repo.py +++ b/backend/lcfs/web/api/other_uses/repo.py @@ -76,7 +76,7 @@ async def get_latest_other_uses_by_group_uuid( ) result = await self.db.execute(query) - return await result.unique().scalars().first() + return result.unique().scalars().first() @repo_handler async def get_other_uses(self, compliance_report_id: int) -> List[OtherUsesSchema]: @@ -310,8 +310,13 @@ 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) - & or_(FuelCode.expiration_date == None, FuelCode.expiration_date > current_date) + or_( + FuelCode.effective_date == None, FuelCode.effective_date <= current_date + ) + & or_( + FuelCode.expiration_date == None, + FuelCode.expiration_date > current_date, + ) & (FuelType.other_uses_fossil_derived == True) ) @@ -333,7 +338,7 @@ async def get_formatted_fuel_types(self) -> List[Dict[str, Any]]: ) result = await self.db.execute(query) - fuel_types = await result.unique().scalars().all() + fuel_types = result.unique().scalars().all() # Prepare the data in the format matching your schema formatted_fuel_types = [] @@ -379,4 +384,4 @@ async def get_formatted_fuel_types(self) -> List[Dict[str, Any]]: ) formatted_fuel_types.append(formatted_fuel_type) - return formatted_fuel_types \ No newline at end of file + return formatted_fuel_types From d13cd66f3d745944f5959b705b7567daf3154770 Mon Sep 17 00:00:00 2001 From: Arturo Reyes Lopez Date: Wed, 11 Dec 2024 12:24:47 -0700 Subject: [PATCH 23/25] fixes in migrations --- .../db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py b/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py index 8ec2b8223..3a512b338 100644 --- a/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py +++ b/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py @@ -11,7 +11,7 @@ # revision identifiers, used by Alembic. revision = "cd8698fe40e6" -down_revision = "9206124a098b" +down_revision = "7ae38a8413ab" branch_labels = None depends_on = None From a7e6979af1f6242cc7e72edbcf7f43dc924936d8 Mon Sep 17 00:00:00 2001 From: Arturo Reyes Lopez Date: Wed, 11 Dec 2024 12:26:11 -0700 Subject: [PATCH 24/25] fix migration issue --- .../db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/lcfs/db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py b/backend/lcfs/db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py index da29c2fc1..0f422117e 100644 --- a/backend/lcfs/db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py +++ b/backend/lcfs/db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py @@ -12,7 +12,7 @@ # revision identifiers, used by Alembic. revision = "7ae38a8413ab" -down_revision = "26ab15f8ab18" +down_revision = "9206124a098b" branch_labels = None depends_on = None From 0253e2ad65b88b158a87701857355eb44cce4f0b Mon Sep 17 00:00:00 2001 From: Arturo Reyes Lopez Date: Wed, 11 Dec 2024 14:17:21 -0700 Subject: [PATCH 25/25] Updating migration to correct down_revision --- .../versions/2024-12-09-22-33_cd8698fe40e6.py | 2 +- ...e38a8413ab.py => 2024-12-09-23-31_7ae38a8413ab.py} | 2 +- backend/lcfs/tests/other_uses/test_other_uses_repo.py | 11 ++++++++--- backend/lcfs/web/api/other_uses/repo.py | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) rename backend/lcfs/db/migrations/versions/{2024-12-09-19-31_7ae38a8413ab.py => 2024-12-09-23-31_7ae38a8413ab.py} (98%) diff --git a/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py b/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py index 3a512b338..8ec2b8223 100644 --- a/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py +++ b/backend/lcfs/db/migrations/versions/2024-12-09-22-33_cd8698fe40e6.py @@ -11,7 +11,7 @@ # revision identifiers, used by Alembic. revision = "cd8698fe40e6" -down_revision = "7ae38a8413ab" +down_revision = "9206124a098b" branch_labels = None depends_on = None diff --git a/backend/lcfs/db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py b/backend/lcfs/db/migrations/versions/2024-12-09-23-31_7ae38a8413ab.py similarity index 98% rename from backend/lcfs/db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py rename to backend/lcfs/db/migrations/versions/2024-12-09-23-31_7ae38a8413ab.py index 0f422117e..51856f8c4 100644 --- a/backend/lcfs/db/migrations/versions/2024-12-09-19-31_7ae38a8413ab.py +++ b/backend/lcfs/db/migrations/versions/2024-12-09-23-31_7ae38a8413ab.py @@ -12,7 +12,7 @@ # revision identifiers, used by Alembic. revision = "7ae38a8413ab" -down_revision = "9206124a098b" +down_revision = "cd8698fe40e6" branch_labels = None depends_on = None diff --git a/backend/lcfs/tests/other_uses/test_other_uses_repo.py b/backend/lcfs/tests/other_uses/test_other_uses_repo.py index 8ef79cd70..67ea7d1d5 100644 --- a/backend/lcfs/tests/other_uses/test_other_uses_repo.py +++ b/backend/lcfs/tests/other_uses/test_other_uses_repo.py @@ -233,9 +233,14 @@ async def test_get_other_use_version_by_user(other_uses_repo, mock_db_session): mock_other_use.version = version mock_other_use.user_type = user_type - mock_db_session.execute.return_value.scalars.return_value.first.return_value = ( - mock_other_use - ) + # Set up mock result chain + mock_result = AsyncMock() + mock_result.scalars = MagicMock(return_value=mock_result) + mock_result.first = MagicMock(return_value=mock_other_use) + + # Configure mock db session + mock_db_session.execute = AsyncMock(return_value=mock_result) + other_uses_repo.db = mock_db_session result = await other_uses_repo.get_other_use_version_by_user( group_uuid, version, user_type diff --git a/backend/lcfs/web/api/other_uses/repo.py b/backend/lcfs/web/api/other_uses/repo.py index 9f49a3d5d..8e515b484 100644 --- a/backend/lcfs/web/api/other_uses/repo.py +++ b/backend/lcfs/web/api/other_uses/repo.py @@ -302,7 +302,7 @@ async def get_other_use_version_by_user( ) result = await self.db.execute(query) - return await result.scalars().first() + return result.scalars().first() @repo_handler async def get_formatted_fuel_types(self) -> List[Dict[str, Any]]: