From eb4c4b96a5a7b04445b7a8053d6caf783cdfd0f1 Mon Sep 17 00:00:00 2001 From: Catalin Oancea Date: Mon, 16 Dec 2024 10:13:43 +0200 Subject: [PATCH 1/4] WIP restoration project computations --- .../modules/calculations/data.repository.ts | 2 + .../custom-projects.controller.ts | 1 + .../custom-projects.service.ts | 2 + .../dto/restoration-project-params.dto.ts | 10 +- .../input-factory/custom-project.factory.ts | 54 ++++++++- .../restoration-project.input.ts | 104 +++++++++++++++++- .../custom-projects-create.spec.ts | 13 ++- 7 files changed, 177 insertions(+), 9 deletions(-) diff --git a/api/src/modules/calculations/data.repository.ts b/api/src/modules/calculations/data.repository.ts index 2f321a9f..0d3498ff 100644 --- a/api/src/modules/calculations/data.repository.ts +++ b/api/src/modules/calculations/data.repository.ts @@ -33,6 +33,7 @@ export type AdditionalBaseData = { communityBenefitSharingFund: BaseDataView['communityBenefitSharingFund']; otherCommunityCashFlow: BaseDataView['otherCommunityCashFlow']; tier1SequestrationRate: BaseDataView['tier1SequestrationRate']; + tier2SequestrationRate: BaseDataView['tier2SequestrationRate']; }; const COMMON_OVERRIDABLE_COST_INPUTS = [ @@ -113,6 +114,7 @@ export class DataRepository extends Repository { 'communityBenefitSharingFund', 'otherCommunityCashFlow', 'tier1SequestrationRate', + 'tier2SequestrationRate', ], }); diff --git a/api/src/modules/custom-projects/custom-projects.controller.ts b/api/src/modules/custom-projects/custom-projects.controller.ts index a00ce809..34edbeb3 100644 --- a/api/src/modules/custom-projects/custom-projects.controller.ts +++ b/api/src/modules/custom-projects/custom-projects.controller.ts @@ -82,6 +82,7 @@ export class CustomProjectsController { return tsRestHandler( customProjectContract.createCustomProject, async () => { + console.log('>>>>>>>>>>>>>> HERE'); const customProject = await this.customProjects.create(dto as any); return { status: 201, diff --git a/api/src/modules/custom-projects/custom-projects.service.ts b/api/src/modules/custom-projects/custom-projects.service.ts index e68daf65..e2eaeda6 100644 --- a/api/src/modules/custom-projects/custom-projects.service.ts +++ b/api/src/modules/custom-projects/custom-projects.service.ts @@ -43,6 +43,7 @@ export class CustomProjectsService extends AppBaseService< } async create(dto: CreateCustomProjectDto): Promise { + console.log('>>>>>>>>>>>>>>> DTO', dto); const { countryCode, ecosystem, activity } = dto; const { additionalBaseData, @@ -74,6 +75,7 @@ export class CustomProjectsService extends AppBaseService< projectInput, costOutput, ); + console.log('>>>>>>>>>>>>>>> CUSTOM PROJECT', customProject); return customProject; } diff --git a/api/src/modules/custom-projects/dto/restoration-project-params.dto.ts b/api/src/modules/custom-projects/dto/restoration-project-params.dto.ts index 6b7ba31c..88f7eb14 100644 --- a/api/src/modules/custom-projects/dto/restoration-project-params.dto.ts +++ b/api/src/modules/custom-projects/dto/restoration-project-params.dto.ts @@ -1 +1,9 @@ -export class RestorationProjectParamsDto {} +import { RESTORATION_ACTIVITY_SUBTYPE } from '@shared/entities/activity.enum'; +import { SEQUESTRATION_RATE_TIER_TYPES } from '@shared/entities/carbon-inputs/sequestration-rate.entity'; + +export class RestorationProjectParamsDto { + activityType: RESTORATION_ACTIVITY_SUBTYPE; + sequestrationRateUsed: SEQUESTRATION_RATE_TIER_TYPES; + projectSpecificSequestrationRate?: number; + plantingSuccessRate: number; +} diff --git a/api/src/modules/custom-projects/input-factory/custom-project.factory.ts b/api/src/modules/custom-projects/input-factory/custom-project.factory.ts index cb5d94a3..18cf8899 100644 --- a/api/src/modules/custom-projects/input-factory/custom-project.factory.ts +++ b/api/src/modules/custom-projects/input-factory/custom-project.factory.ts @@ -10,6 +10,8 @@ import { CostOutput } from '@api/modules/calculations/calculation.engine'; import { ProjectInput } from '@api/modules/calculations/cost.calculator'; import { CustomProject } from '@shared/entities/custom-project.entity'; import { Country } from '@shared/entities/country.entity'; +import { RestorationProjectParamsDto } from '../dto/restoration-project-params.dto'; +import { RestorationProjectInput } from '@api/modules/custom-projects/input-factory/restoration-project.input'; export type ConservationProjectCarbonInputs = { lossRate: number; @@ -42,12 +44,62 @@ export class CustomProjectFactory { additionalAssumptions, ); } else if (dto.activity === ACTIVITY.RESTORATION) { - throw new NotImplementedException('Restoration not implemented'); + return this.createRestorationProjectInput( + dto, + additionalBaseData, + additionalAssumptions, + ); } else { throw new Error('Invalid activity type'); } } + private createRestorationProjectInput( + dto: CreateCustomProjectDto, + additionalBaseData: AdditionalBaseData, + additionalAssumptions: NonOverridableModelAssumptions, + ) { + const { + parameters, + assumptions, + costInputs, + projectName, + projectSizeHa, + initialCarbonPriceAssumption, + activity, + carbonRevenuesToCover, + ecosystem, + countryCode, + } = dto; + + const projectParams = parameters as RestorationProjectParamsDto; + + const restorationProjectInput: RestorationProjectInput = + new RestorationProjectInput(); + restorationProjectInput.setGeneralInputs({ + projectName, + projectSizeHa, + initialCarbonPriceAssumption, + activity, + carbonRevenuesToCover, + ecosystem, + countryCode, + }); + restorationProjectInput.setSequestrationRate( + projectParams, + additionalBaseData, + ); + restorationProjectInput.setCostAndCarbonInputs( + costInputs, + additionalBaseData, + ); + restorationProjectInput.setModelAssumptions( + assumptions, + additionalAssumptions, + ); + return restorationProjectInput; + } + private createConservationProjectInput( dto: CreateCustomProjectDto, additionalBaseData: AdditionalBaseData, diff --git a/api/src/modules/custom-projects/input-factory/restoration-project.input.ts b/api/src/modules/custom-projects/input-factory/restoration-project.input.ts index f431bd11..94757e09 100644 --- a/api/src/modules/custom-projects/input-factory/restoration-project.input.ts +++ b/api/src/modules/custom-projects/input-factory/restoration-project.input.ts @@ -1,4 +1,102 @@ -import { ConservationProjectInput } from '@api/modules/custom-projects/input-factory/conservation-project.input'; +import { + ACTIVITY, + RESTORATION_ACTIVITY_SUBTYPE, +} from '@shared/entities/activity.enum'; +import { ECOSYSTEM } from '@shared/entities/ecosystem.enum'; +import { CARBON_REVENUES_TO_COVER } from '@api/modules/custom-projects/dto/create-custom-project-dto'; +import { OverridableCostInputs } from '@shared/dtos/custom-projects/cost.inputs'; +import { AdditionalBaseData } from '@api/modules/calculations/data.repository'; +import { + ModelAssumptionsForCalculations, + NonOverridableModelAssumptions, +} from '@api/modules/calculations/assumptions.repository'; +import { SEQUESTRATION_RATE_TIER_TYPES } from '@shared/entities/carbon-inputs/sequestration-rate.entity'; +import { RestorationProjectParamsDto } from '@api/modules/custom-projects/dto/restoration-project-params.dto'; +import { GeneralProjectInputs } from '@api/modules/custom-projects/input-factory/custom-project.factory'; +import { OverridableAssumptions } from '../dto/project-assumptions.dto'; -// TMP extends ConservationProjectInput, until we know how to implement the RestorationProjectInput -export class RestorationProjectInput extends ConservationProjectInput {} +export class RestorationProjectInput { + countryCode: string; + + projectName: string; + + activity: ACTIVITY; + + ecosystem: ECOSYSTEM; + + projectSizeHa: number; + + initialCarbonPriceAssumption: number; + + carbonRevenuesToCover: CARBON_REVENUES_TO_COVER; + + activityType: RESTORATION_ACTIVITY_SUBTYPE; + + sequestrationRate: number; + + costAndCarbonInputs: OverridableCostInputs & AdditionalBaseData; + + lossRate: number; + + emissionFactor: number; + + emissionFactorAgb: number; + + emissionFactorSoc: number; + + assumptions: ModelAssumptionsForCalculations; + + setSequestrationRate( + parameters: RestorationProjectParamsDto, + additionalBaseData: AdditionalBaseData, + ): this { + if ( + parameters.sequestrationRateUsed === SEQUESTRATION_RATE_TIER_TYPES.TIER_3 + ) { + this.sequestrationRate = parameters.projectSpecificSequestrationRate; + } + + if ( + parameters.sequestrationRateUsed === SEQUESTRATION_RATE_TIER_TYPES.TIER_1 + ) { + this.sequestrationRate = additionalBaseData.tier1SequestrationRate; + } + if ( + parameters.sequestrationRateUsed === SEQUESTRATION_RATE_TIER_TYPES.TIER_2 + ) { + this.sequestrationRate = additionalBaseData.tier2SequestrationRate; + } + return this; + } + + setCostAndCarbonInputs( + overridableCostInputs: OverridableCostInputs, + additionalBaseData: AdditionalBaseData, + ): this { + this.costAndCarbonInputs = { + ...overridableCostInputs, + ...additionalBaseData, + }; + return this; + } + + setModelAssumptions( + overridableAssumptions: OverridableAssumptions, + rest: NonOverridableModelAssumptions, + ): this { + this.assumptions = { ...overridableAssumptions, ...rest }; + return this; + } + + setGeneralInputs(generalInputs: GeneralProjectInputs): this { + this.projectName = generalInputs.projectName; + this.countryCode = generalInputs.countryCode; + this.activity = generalInputs.activity; + this.ecosystem = generalInputs.ecosystem; + this.projectSizeHa = generalInputs.projectSizeHa; + this.initialCarbonPriceAssumption = + generalInputs.initialCarbonPriceAssumption; + this.carbonRevenuesToCover = generalInputs.carbonRevenuesToCover; + return this; + } +} diff --git a/api/test/integration/custom-projects/custom-projects-create.spec.ts b/api/test/integration/custom-projects/custom-projects-create.spec.ts index b28b4347..c44c6e38 100644 --- a/api/test/integration/custom-projects/custom-projects-create.spec.ts +++ b/api/test/integration/custom-projects/custom-projects-create.spec.ts @@ -16,21 +16,25 @@ describe('Create Custom Projects - Setup', () => { await testManager.close(); }); - describe.skip('TEMPORAL, FOR REFERENCE', () => { - test('Should generate a conservation project input object that will be used for calculations', async () => { + describe('TEMPORAL, FOR REFERENCE', () => { + test.only('Should generate a conservation project input object that will be used for calculations', async () => { const response = await testManager .request() .post(customProjectContract.createCustomProject.path) .send({ countryCode: 'IND', - activity: 'Conservation', + activity: 'Restoration', ecosystem: 'Mangrove', projectName: 'My custom project', projectSizeHa: 1000, initialCarbonPriceAssumption: 1000, carbonRevenuesToCover: 'Opex', + activityType: 'Planting', + plantingSuccessRate: 0.8, parameters: { lossRateUsed: 'National average', + sequestrationRateUsed: 'Tier 3 - Project-specific rate', + projectSpecificSequestrationRate: 100, emissionFactorUsed: 'Tier 2 - Country-specific emission factor', }, costInputs: { @@ -43,6 +47,7 @@ describe('Create Custom Projects - Setup', () => { financingCost: 0.05, validation: 50000, implementationLaborHybrid: null, + implementationLabor: 0, monitoring: 15000, maintenance: 0.0833, carbonStandardFees: 0.2, @@ -61,7 +66,7 @@ describe('Create Custom Projects - Setup', () => { projectLength: 20, }, }); - + // console.log(response); // TODO: Write tests for cost calculations }); }); From 8d0af72bccc35ee65943002b606f3bead2d14cba Mon Sep 17 00:00:00 2001 From: Catalin Oancea Date: Mon, 16 Dec 2024 10:14:44 +0200 Subject: [PATCH 2/4] Remove comment --- api/src/modules/custom-projects/custom-projects.controller.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/api/src/modules/custom-projects/custom-projects.controller.ts b/api/src/modules/custom-projects/custom-projects.controller.ts index 34edbeb3..a00ce809 100644 --- a/api/src/modules/custom-projects/custom-projects.controller.ts +++ b/api/src/modules/custom-projects/custom-projects.controller.ts @@ -82,7 +82,6 @@ export class CustomProjectsController { return tsRestHandler( customProjectContract.createCustomProject, async () => { - console.log('>>>>>>>>>>>>>> HERE'); const customProject = await this.customProjects.create(dto as any); return { status: 201, From fd86db591f6b465972da0b4bfd26ed8173929fe0 Mon Sep 17 00:00:00 2001 From: Catalin Oancea Date: Tue, 17 Dec 2024 08:50:16 +0200 Subject: [PATCH 3/4] Skip test --- api/src/modules/custom-projects/custom-projects.service.ts | 2 -- .../custom-projects/custom-projects-create.spec.ts | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/api/src/modules/custom-projects/custom-projects.service.ts b/api/src/modules/custom-projects/custom-projects.service.ts index e2eaeda6..e68daf65 100644 --- a/api/src/modules/custom-projects/custom-projects.service.ts +++ b/api/src/modules/custom-projects/custom-projects.service.ts @@ -43,7 +43,6 @@ export class CustomProjectsService extends AppBaseService< } async create(dto: CreateCustomProjectDto): Promise { - console.log('>>>>>>>>>>>>>>> DTO', dto); const { countryCode, ecosystem, activity } = dto; const { additionalBaseData, @@ -75,7 +74,6 @@ export class CustomProjectsService extends AppBaseService< projectInput, costOutput, ); - console.log('>>>>>>>>>>>>>>> CUSTOM PROJECT', customProject); return customProject; } diff --git a/api/test/integration/custom-projects/custom-projects-create.spec.ts b/api/test/integration/custom-projects/custom-projects-create.spec.ts index c44c6e38..c532783f 100644 --- a/api/test/integration/custom-projects/custom-projects-create.spec.ts +++ b/api/test/integration/custom-projects/custom-projects-create.spec.ts @@ -16,8 +16,8 @@ describe('Create Custom Projects - Setup', () => { await testManager.close(); }); - describe('TEMPORAL, FOR REFERENCE', () => { - test.only('Should generate a conservation project input object that will be used for calculations', async () => { + describe.skip('TEMPORAL, FOR REFERENCE', () => { + test('Should generate a conservation project input object that will be used for calculations', async () => { const response = await testManager .request() .post(customProjectContract.createCustomProject.path) From ecf50af040415b3bd21416777c5167adc34c3bbf Mon Sep 17 00:00:00 2001 From: Catalin Oancea Date: Tue, 17 Dec 2024 09:11:41 +0200 Subject: [PATCH 4/4] Log response --- .../integration/custom-projects/custom-projects-create.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/test/integration/custom-projects/custom-projects-create.spec.ts b/api/test/integration/custom-projects/custom-projects-create.spec.ts index c532783f..9d1b9249 100644 --- a/api/test/integration/custom-projects/custom-projects-create.spec.ts +++ b/api/test/integration/custom-projects/custom-projects-create.spec.ts @@ -66,7 +66,7 @@ describe('Create Custom Projects - Setup', () => { projectLength: 20, }, }); - // console.log(response); + console.log(response); // TODO: Write tests for cost calculations }); });