Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RELEASE] Tacky Teak #18

Merged
merged 19 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8659cc1
Merge pull request #15 from wri/main
roguenet Nov 14, 2024
b5b0fe2
[TM-1452] Add documentation for includeTestProjects
roguenet Nov 15, 2024
b0b7812
[TM-1452] Implement includeTestProjects filter.
roguenet Nov 18, 2024
26a8622
[TM-1452] Implement polygon status filter.
roguenet Nov 18, 2024
412ac96
[TM-1452] Implement filtering on project UUID
roguenet Nov 18, 2024
81b53a8
[TM-1452] Implement lastModifiedDate filter.
roguenet Nov 18, 2024
d4951df
[TM-1452] Implement missing indicator filter.
roguenet Nov 19, 2024
1884773
[TM-1452] streamline simple filters.
roguenet Nov 19, 2024
5c0b434
[TM-1452] Implement boundary polygon filter.
roguenet Nov 19, 2024
cf9f0a8
[TM-1452] Be picky about attributes needed to greatly reduce the quer…
roguenet Nov 19, 2024
93796bc
[TM-1452] Updated controller specs
roguenet Nov 20, 2024
2a93aee
[TM-1452] Unit tests for all filters except touchesBoundary
roguenet Nov 20, 2024
40ede90
[TM-1452] touches boundary and multiple filters tests.
roguenet Nov 20, 2024
a5aa995
[TM-1452] Sync database by running one isolated test first.
roguenet Nov 20, 2024
a2ead28
Merge pull request #16 from wri/feat/TM-1452-site-polygon-filtering
roguenet Nov 22, 2024
c581eb4
[TM-1453] Implement bulk indicator upload.
roguenet Nov 22, 2024
2d055d1
[TM-1453] Tests for the bulk update endpoint and update indicator ser…
roguenet Nov 22, 2024
2fa9abc
[TM-1453] The database test needs to run without cache so it does its…
roguenet Nov 22, 2024
6156e11
Merge pull request #17 from wri/feat/TM-1453-bulk-update-indicators
roguenet Nov 25, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ jobs:
with:
fetch-depth: 0

- uses: KengoTODA/actions-setup-docker-compose@v1
with:
version: '2.29.1'

# This enables task distribution via Nx Cloud
# Run this command as early as possible, before dependencies are installed
# Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun
Expand All @@ -33,10 +37,16 @@ jobs:

- run: NX_CLOUD_DISTRIBUTED_EXECUTION=false npx nx run-many -t lint build

- uses: KengoTODA/actions-setup-docker-compose@v1
with:
version: '2.29.1'
- name: Bring up DB Docker Container
run: |
docker-compose up -d
./docker/test-connection.sh

- run: docker-compose up -d
# First run just the small database test to get the test database synced to the current schema
# 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 --no-cache

- run: NX_CLOUD_DISTRIBUTED_EXECUTION=false npx nx run-many -t test --coverage --passWithNoTests
- 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
Expand Up @@ -46,7 +46,7 @@ export class SitePolygonQueryDto {
name: "projectId[]",
isArray: true,
required: false,
description: "Filter results by project UUID(s)"
description: "Filter results by project UUID(s). If specified, the includeTestProjects param is ignored"
})
@IsOptional()
@IsArray()
Expand All @@ -73,11 +73,19 @@ export class SitePolygonQueryDto {

@ApiProperty({
required: false,
description: "Filter results by polygons that are within the boundary of the polygon referenced by this UUID"
description: "Filter results by polygons that intersect with the boundary of the polygon referenced by this UUID"
})
@IsOptional()
boundaryPolygon?: string;

@ApiProperty({
required: false,
default: false,
description:
"Include polygons for test projects in the results. If an explicit list of project UUIDs is included in projectId[], this parameter is ignored."
})
includeTestProjects?: boolean;

@ApiProperty({ name: "page", required: false, description: "Pagination information" })
@ValidateNested()
@IsOptional()
Expand Down
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
Loading