Skip to content

Commit

Permalink
Merge pull request #17 from wri/feat/TM-1453-bulk-update-indicators
Browse files Browse the repository at this point in the history
[TM-1453] Bulk update indicators
  • Loading branch information
roguenet authored Nov 25, 2024
2 parents a2ead28 + 2fa9abc commit 6156e11
Show file tree
Hide file tree
Showing 8 changed files with 468 additions and 193 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
# in a clean way. For some reason, the `run-many` is necessary here. If this line simply uses
# nx test database, the connection to the DB gets cut off before the sync is complete.
- name: Sync DB Schema
run: NX_CLOUD_DISTRIBUTED_EXECUTION=false npx nx test database
run: NX_CLOUD_DISTRIBUTED_EXECUTION=false npx nx test database --no-cache

- name: Test all
run: NX_CLOUD_DISTRIBUTED_EXECUTION=false npx nx run-many -t test --coverage --passWithNoTests
68 changes: 56 additions & 12 deletions apps/research-service/src/site-polygons/dto/indicators.dto.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { ApiProperty } from "@nestjs/swagger";
import { INDICATORS } from "@terramatch-microservices/database/constants";
import { IsInt, IsNotEmpty, IsNumber, IsOptional, IsString } from "class-validator";

export class IndicatorTreeCoverLossDto {
@ApiProperty({ enum: [INDICATORS[2], INDICATORS[3]] })
indicatorSlug: (typeof INDICATORS)[2] | (typeof INDICATORS)[3];

@ApiProperty({ example: "2024" })
@IsInt()
@ApiProperty({ example: 2024 })
yearOfAnalysis: number;

@IsNotEmpty()
@ApiProperty({
type: "object",
description: "Mapping of year of analysis to value.",
Expand All @@ -20,45 +23,63 @@ export class IndicatorHectaresDto {
@ApiProperty({ enum: [INDICATORS[4], INDICATORS[5], INDICATORS[6]] })
indicatorSlug: (typeof INDICATORS)[4] | (typeof INDICATORS)[5] | (typeof INDICATORS)[6];

@IsInt()
@ApiProperty({ example: "2024" })
yearOfAnalysis: number;

@IsNotEmpty()
@ApiProperty({
type: "object",
description: "Mapping of area type (eco region, land use, etc) to hectares",
example: { "Northern Acacia-Commiphora bushlands and thickets": 0.104 }
})
value: Record<string, number>;
}

export class IndicatorTreeCountDto {
@ApiProperty({ enum: [INDICATORS[7], INDICATORS[8]] })
indicatorSlug: (typeof INDICATORS)[7] | (typeof INDICATORS)[8];

@IsInt()
@ApiProperty({ example: "2024" })
yearOfAnalysis: number;

@IsString()
@IsOptional()
@ApiProperty()
surveyType: string | null;

@IsNumber()
@IsOptional()
@ApiProperty()
surveyId: number | null;

@IsNumber()
@IsOptional()
@ApiProperty()
treeCount: number | null;

@IsString()
@IsOptional()
@ApiProperty({ example: "types TBD" })
uncertaintyType: string | null;

@IsString()
@IsOptional()
@ApiProperty()
imagerySource: string | null;

@IsString()
@IsOptional()
@ApiProperty({ type: "url" })
imageryId: string | null;

@IsString()
@IsOptional()
@ApiProperty()
projectPhase: string | null;

@IsNumber()
@IsOptional()
@ApiProperty()
confidence: number | null;
}
Expand All @@ -67,15 +88,22 @@ export class IndicatorTreeCoverDto {
@ApiProperty({ enum: [INDICATORS[1]] })
indicatorSlug: (typeof INDICATORS)[1];

@IsInt()
@ApiProperty({ example: "2024" })
yearOfAnalysis: number;

@IsString()
@IsOptional()
@ApiProperty({ example: "2024" })
projectPhase: string | null;

@IsNumber()
@IsOptional()
@ApiProperty()
percentCover: number | null;

@IsNumber()
@IsOptional()
@ApiProperty()
plusMinusPercent: number | null;
}
Expand All @@ -84,18 +112,27 @@ export class IndicatorFieldMonitoringDto {
@ApiProperty({ enum: [INDICATORS[9]] })
indicatorSlug: (typeof INDICATORS)[9];

@IsInt()
@ApiProperty({ example: "2024" })
yearOfAnalysis: number;

@IsNumber()
@IsOptional()
@ApiProperty()
treeCount: number | null;

@IsString()
@IsOptional()
@ApiProperty()
projectPhase: string | null;

@IsString()
@IsOptional()
@ApiProperty()
species: string | null;

@IsNumber()
@IsOptional()
@ApiProperty()
survivalRate: number | null;
}
Expand All @@ -104,28 +141,35 @@ export class IndicatorMsuCarbonDto {
@ApiProperty({ enum: [INDICATORS[10]] })
indicatorSlug: (typeof INDICATORS)[10];

@IsInt()
@ApiProperty({ example: "2024" })
yearOfAnalysis: number;

@IsNumber()
@IsOptional()
@ApiProperty()
carbonOutput: number | null;

@IsString()
@IsOptional()
@ApiProperty()
projectPhase: string | null;

@IsNumber()
@IsOptional()
@ApiProperty()
confidence: number | null;
}

export const INDICATOR_DTOS = {
[INDICATORS[1]]: IndicatorTreeCoverDto.prototype,
[INDICATORS[2]]: IndicatorTreeCoverLossDto.prototype,
[INDICATORS[3]]: IndicatorTreeCoverLossDto.prototype,
[INDICATORS[4]]: IndicatorHectaresDto.prototype,
[INDICATORS[5]]: IndicatorHectaresDto.prototype,
[INDICATORS[6]]: IndicatorHectaresDto.prototype,
[INDICATORS[7]]: IndicatorTreeCountDto.prototype,
[INDICATORS[8]]: IndicatorTreeCountDto.prototype,
[INDICATORS[9]]: IndicatorFieldMonitoringDto.prototype,
[INDICATORS[10]]: IndicatorMsuCarbonDto.prototype
[INDICATORS[1]]: IndicatorTreeCoverDto,
[INDICATORS[2]]: IndicatorTreeCoverLossDto,
[INDICATORS[3]]: IndicatorTreeCoverLossDto,
[INDICATORS[4]]: IndicatorHectaresDto,
[INDICATORS[5]]: IndicatorHectaresDto,
[INDICATORS[6]]: IndicatorHectaresDto,
[INDICATORS[7]]: IndicatorTreeCountDto,
[INDICATORS[8]]: IndicatorTreeCountDto,
[INDICATORS[9]]: IndicatorFieldMonitoringDto,
[INDICATORS[10]]: IndicatorMsuCarbonDto
};
Original file line number Diff line number Diff line change
@@ -1,48 +1,55 @@
import { ApiProperty } from '@nestjs/swagger';
import {
IndicatorFieldMonitoringDto,
IndicatorHectaresDto, IndicatorMsuCarbonDto,
IndicatorTreeCountDto, IndicatorTreeCoverDto,
IndicatorTreeCoverLossDto
} from './indicators.dto';
import { ApiProperty } from "@nestjs/swagger";
import { IndicatorDto } from "./site-polygon.dto";
import { Equals, IsArray, IsUUID, ValidateNested } from "class-validator";
import { Type } from "class-transformer";
import { INDICATOR_DTOS } from "./indicators.dto";

class SitePolygonUpdateAttributes {
@IsArray()
@ValidateNested()
@Type(() => Object, {
keepDiscriminatorProperty: true,
discriminator: {
property: "indicatorSlug",
subTypes: Object.entries(INDICATOR_DTOS).map(([name, value]) => ({ name, value }))
}
})
@ApiProperty({
type: 'array',
type: "array",
items: {
oneOf: [
{ $ref: '#/components/schemas/IndicatorTreeCoverLossDto' },
{ $ref: '#/components/schemas/IndicatorHectaresDto' },
{ $ref: '#/components/schemas/IndicatorTreeCountDto' },
{ $ref: '#/components/schemas/IndicatorTreeCoverDto' },
{ $ref: '#/components/schemas/IndicatorFieldMonitoringDto' },
{ $ref: '#/components/schemas/IndicatorMsuCarbonDto' },
{ $ref: "#/components/schemas/IndicatorTreeCoverLossDto" },
{ $ref: "#/components/schemas/IndicatorHectaresDto" },
{ $ref: "#/components/schemas/IndicatorTreeCountDto" },
{ $ref: "#/components/schemas/IndicatorTreeCoverDto" },
{ $ref: "#/components/schemas/IndicatorFieldMonitoringDto" },
{ $ref: "#/components/schemas/IndicatorMsuCarbonDto" }
]
},
description: 'All indicators to update for this polygon'
description: "All indicators to update for this polygon"
})
indicators: (
IndicatorTreeCoverLossDto |
IndicatorHectaresDto |
IndicatorTreeCountDto |
IndicatorTreeCoverDto |
IndicatorFieldMonitoringDto |
IndicatorMsuCarbonDto
)[];
indicators: IndicatorDto[];
}

class SitePolygonUpdate {
@ApiProperty({ enum: ['sitePolygons'] })
type: 'sitePolygons';
@Equals("sitePolygons")
@ApiProperty({ enum: ["sitePolygons"] })
type: string;

@ApiProperty({ format: 'uuid' })
@IsUUID()
@ApiProperty({ format: "uuid" })
id: string;

@ValidateNested()
@Type(() => SitePolygonUpdateAttributes)
@ApiProperty({ type: () => SitePolygonUpdateAttributes })
attributes: SitePolygonUpdateAttributes;
}

export class SitePolygonBulkUpdateBodyDto {
@IsArray()
@ValidateNested()
@Type(() => SitePolygonUpdate)
@ApiProperty({ isArray: true, type: () => SitePolygonUpdate })
data: SitePolygonUpdate[];
}
Loading

0 comments on commit 6156e11

Please sign in to comment.