Skip to content

Commit

Permalink
feat(api): Add CustomProject->getActivityTypesDefaults contract and i…
Browse files Browse the repository at this point in the history
…mplement the necessary stuff to make it work
  • Loading branch information
alepefe committed Dec 9, 2024
1 parent db1f577 commit 8b4e745
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 1 deletion.
57 changes: 57 additions & 0 deletions api/src/modules/calculations/data.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { OverridableCostInputs } from '@api/modules/custom-projects/dto/project-
import { BaseSize } from '@shared/entities/base-size.entity';
import { BaseIncrease } from '@shared/entities/base-increase.entity';
import { AssumptionsRepository } from '@api/modules/calculations/assumptions.repository';
import { GetActivityTypesDefaults } from '@shared/dtos/custom-projects/get-activity-types-defaults.dto';

/**
* Additional data that is required to perform calculations, which is not overridable by the user. Better naming and clustering of concepts would be great
Expand Down Expand Up @@ -110,6 +111,62 @@ export class DataRepository extends Repository<BaseDataView> {
return additionalBaseData;
}

async getActivityTypesDefaults(dto: GetActivityTypesDefaults) {
const defaults = {};

[defaults[ACTIVITY.CONSERVATION], defaults[ACTIVITY.RESTORATION]] =
await Promise.all([
this.getConservationActivityDefaults(dto),
this.getRestorationActivityDefaults(dto),
]);

return defaults;
}

private async getConservationActivityDefaults(dto: GetActivityTypesDefaults) {
const { countryCode, ecosystem } = dto;

const result = await this.findOne({
where: { countryCode, ecosystem, activity: ACTIVITY.CONSERVATION },
select: [
'ecosystemLossRate',
'tier1EmissionFactor',
'emissionFactorAgb',
'emissionFactorSoc',
],
});
if (result === null) return null;

return {
ecosystemLossRate: result.ecosystemLossRate,
emissionFactor: {
tier1: result.tier1EmissionFactor,
tier2: {
emissionFactorAgb: result.emissionFactorAgb,
emissionFactorSoc: result.emissionFactorSoc,
},
},
};
}

private async getRestorationActivityDefaults(dto: GetActivityTypesDefaults) {
const { countryCode, ecosystem } = dto;

const result = await this.findOne({
where: { countryCode, ecosystem, activity: ACTIVITY.RESTORATION },
select: ['activity', 'tier1SequestrationRate', 'tier2SequestrationRate'],
});
if (result === null) return null;

return {
activity: result.activity,
sequestrationRate: {
tier1: result.tier1SequestrationRate,
tier2: result.tier2SequestrationRate,
},
};
}

async getOverridableCostInputs(
dto: GetOverridableCostInputs,
): Promise<OverridableCostInputs> {
Expand Down
11 changes: 11 additions & 0 deletions api/src/modules/custom-projects/custom-projects.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ export class CustomProjectsController {
private readonly customProjects: CustomProjectsService,
) {}

@TsRestHandler(customProjectContract.getActivityTypesDefaults)
async getActivityTypeDefaults(): Promise<ControllerResponse> {
return tsRestHandler(
customProjectContract.getActivityTypesDefaults,
async ({ query }) => {
const data = await this.customProjects.getActivityTypeDefaults(query);
return { body: { data }, status: HttpStatus.OK };
},
);
}

@TsRestHandler(customProjectContract.getAvailableCountries)
async getAvailableCountriesToCreateACustomProject(): Promise<ControllerResponse> {
return tsRestHandler(
Expand Down
5 changes: 5 additions & 0 deletions api/src/modules/custom-projects/custom-projects.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { User } from '@shared/entities/users/user.entity';
import { EventBus } from '@nestjs/cqrs';
import { SaveCustomProjectEvent } from '@api/modules/custom-projects/events/save-custom-project.event';
import { FetchSpecification } from 'nestjs-base-service';
import { GetActivityTypesDefaults } from '@shared/dtos/custom-projects/get-activity-types-defaults.dto';

@Injectable()
export class CustomProjectsService extends AppBaseService<
Expand Down Expand Up @@ -87,6 +88,10 @@ export class CustomProjectsService extends AppBaseService<
}
}

async getActivityTypeDefaults(dto: GetActivityTypesDefaults) {
return this.dataRepository.getActivityTypesDefaults(dto);
}

async getDefaultCostInputs(
dto: GetOverridableCostInputs,
): Promise<OverridableCostInputs> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,21 @@ describe('Create Custom Projects - Setup', () => {
await testManager.close();
});

describe('Get Overridable Model Assumptions', () => {
describe('Get Activity type defaults', () => {
test('Should return activity type defaults based on country and ecosystem', async () => {
const response = await testManager
.request()
.get(customProjectContract.getActivityTypesDefaults.path)
.query({
countryCode: 'IND',
ecosystem: ECOSYSTEM.MANGROVE,
});

expect(response.status).toBe(200);
});
});

describe.skip('Get Overridable Model Assumptions', () => {
test('Should return overridable model assumptions based on ecosystem and activity', async () => {
const response = await testManager
.request()
Expand Down
11 changes: 11 additions & 0 deletions shared/contracts/custom-projects.contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,23 @@ import { OverridableCostInputs } from "@api/modules/custom-projects/dto/project-
import { GetAssumptionsSchema } from "@shared/schemas/assumptions/get-assumptions.schema";
import { ModelAssumptions } from "@shared/entities/model-assumptions.entity";
import { generateEntityQuerySchema } from "@shared/schemas/query-param.schema";
import { GetActivityTypesDefaultsSchema } from "@shared/schemas/custom-projects/activity-types-defaults.schema";

export const customProjecsQuerySchema =
generateEntityQuerySchema(CustomProject);

const contract = initContract();
export const customProjectContract = contract.router({
getActivityTypesDefaults: {
method: "GET",
path: "/custom-projects/activity-types-defaults",
query: GetActivityTypesDefaultsSchema,
responses: {
200: contract.type<ApiResponse<any>>(),
},
summary:
"Get default values for fields based on the selected country and ecosystem",
},
getAvailableCountries: {
method: "GET",
path: "/custom-projects/available-countries",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { GetActivityTypesDefaultsSchema } from "@shared/schemas/custom-projects/activity-types-defaults.schema";
import { z } from "zod";

export type GetActivityTypesDefaults = z.infer<
typeof GetActivityTypesDefaultsSchema
>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { z } from "zod";
import { ECOSYSTEM } from "@shared/entities/ecosystem.enum";

export const GetActivityTypesDefaultsSchema = z.object({
countryCode: z.string().min(3).max(3),
ecosystem: z.nativeEnum(ECOSYSTEM),
});

0 comments on commit 8b4e745

Please sign in to comment.