diff --git a/services/API-service/migration/1732538758226-StaticDataActiveBoolean.ts b/services/API-service/migration/1732538758226-StaticDataActiveBoolean.ts new file mode 100644 index 000000000..18cfcd35f --- /dev/null +++ b/services/API-service/migration/1732538758226-StaticDataActiveBoolean.ts @@ -0,0 +1,104 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class StaticDataActiveBoolean1732538758226 + implements MigrationInterface +{ + name = 'StaticDataActiveBoolean1732538758226'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "IBF-app"."point-data" ADD "active" boolean NOT NULL DEFAULT true`, + ); + await queryRunner.query( + `ALTER TABLE "IBF-app"."lines-data" ADD "active" boolean NOT NULL DEFAULT true`, + ); + + // Recreate lines-data views with filter on active column + await queryRunner.query( + `DELETE FROM "IBF-app"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, + ['VIEW', 'roads_exposure_per_lead_time', 'IBF-app'], + ); + await queryRunner.query( + `DROP VIEW "IBF-app"."roads_exposure_per_lead_time"`, + ); + await queryRunner.query( + `DELETE FROM "IBF-app"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, + ['VIEW', 'buildings_exposure_per_lead_time', 'IBF-app'], + ); + await queryRunner.query( + `DROP VIEW "IBF-app"."buildings_exposure_per_lead_time"`, + ); + await queryRunner.query( + `CREATE VIEW "IBF-app"."buildings_exposure_per_lead_time" AS SELECT line."referenceId",line.geom, status."leadTime", COALESCE("status"."exposed",FALSE) as "exposed" FROM "IBF-app"."lines-data" "line" LEFT JOIN "IBF-app"."lines-data-dynamic-status" "status" ON line."linesDataId" = status."referenceId" LEFT JOIN (SELECT status."leadTime" as "leadTime", MAX(timestamp) as max_timestamp FROM "IBF-app"."lines-data-dynamic-status" "status" LEFT JOIN "IBF-app"."lines-data" "line" ON line."linesDataId" = status."referenceId" WHERE line."linesDataCategory" = 'buildings' GROUP BY status."leadTime") "max_timestamp" ON status."leadTime" = max_timestamp."leadTime" WHERE line."linesDataCategory" = 'buildings' AND "line"."active" = true AND ("status"."timestamp" = max_timestamp.max_timestamp OR "status"."timestamp" IS NULL)`, + ); + await queryRunner.query( + `INSERT INTO "IBF-app"."typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, + [ + 'IBF-app', + 'VIEW', + 'buildings_exposure_per_lead_time', + 'SELECT line."referenceId",line.geom, status."leadTime", COALESCE("status"."exposed",FALSE) as "exposed" FROM "IBF-app"."lines-data" "line" LEFT JOIN "IBF-app"."lines-data-dynamic-status" "status" ON line."linesDataId" = status."referenceId" LEFT JOIN (SELECT status."leadTime" as "leadTime", MAX(timestamp) as max_timestamp FROM "IBF-app"."lines-data-dynamic-status" "status" LEFT JOIN "IBF-app"."lines-data" "line" ON line."linesDataId" = status."referenceId" WHERE line."linesDataCategory" = \'buildings\' GROUP BY status."leadTime") "max_timestamp" ON status."leadTime" = max_timestamp."leadTime" WHERE line."linesDataCategory" = \'buildings\' AND "line"."active" = true AND ("status"."timestamp" = max_timestamp.max_timestamp OR "status"."timestamp" IS NULL)', + ], + ); + await queryRunner.query( + `CREATE VIEW "IBF-app"."roads_exposure_per_lead_time" AS SELECT line."referenceId",line.geom,line.attributes->>'highway' as "highway", status."leadTime", COALESCE("status"."exposed",FALSE) as "exposed" FROM "IBF-app"."lines-data" "line" LEFT JOIN "IBF-app"."lines-data-dynamic-status" "status" ON line."linesDataId" = status."referenceId" LEFT JOIN (SELECT status."leadTime" as "leadTime", MAX(timestamp) as max_timestamp FROM "IBF-app"."lines-data-dynamic-status" "status" LEFT JOIN "IBF-app"."lines-data" "line" ON line."linesDataId" = status."referenceId" WHERE line."linesDataCategory" = 'roads' GROUP BY status."leadTime") "max_timestamp" ON status."leadTime" = max_timestamp."leadTime" WHERE line."linesDataCategory" = 'roads' AND "line"."active" = true AND ("status"."timestamp" = max_timestamp.max_timestamp OR "status"."timestamp" IS NULL)`, + ); + await queryRunner.query( + `INSERT INTO "IBF-app"."typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, + [ + 'IBF-app', + 'VIEW', + 'roads_exposure_per_lead_time', + 'SELECT line."referenceId",line.geom,line.attributes->>\'highway\' as "highway", status."leadTime", COALESCE("status"."exposed",FALSE) as "exposed" FROM "IBF-app"."lines-data" "line" LEFT JOIN "IBF-app"."lines-data-dynamic-status" "status" ON line."linesDataId" = status."referenceId" LEFT JOIN (SELECT status."leadTime" as "leadTime", MAX(timestamp) as max_timestamp FROM "IBF-app"."lines-data-dynamic-status" "status" LEFT JOIN "IBF-app"."lines-data" "line" ON line."linesDataId" = status."referenceId" WHERE line."linesDataCategory" = \'roads\' GROUP BY status."leadTime") "max_timestamp" ON status."leadTime" = max_timestamp."leadTime" WHERE line."linesDataCategory" = \'roads\' AND "line"."active" = true AND ("status"."timestamp" = max_timestamp.max_timestamp OR "status"."timestamp" IS NULL)', + ], + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `DELETE FROM "IBF-app"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, + ['VIEW', 'roads_exposure_per_lead_time', 'IBF-app'], + ); + await queryRunner.query( + `DROP VIEW "IBF-app"."roads_exposure_per_lead_time"`, + ); + await queryRunner.query( + `DELETE FROM "IBF-app"."typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "schema" = $3`, + ['VIEW', 'buildings_exposure_per_lead_time', 'IBF-app'], + ); + await queryRunner.query( + `DROP VIEW "IBF-app"."buildings_exposure_per_lead_time"`, + ); + await queryRunner.query( + `CREATE VIEW "IBF-app"."buildings_exposure_per_lead_time" AS SELECT line."referenceId",line.geom, status."leadTime", COALESCE("status"."exposed",FALSE) as "exposed" FROM "IBF-app"."lines-data" "line" LEFT JOIN "IBF-app"."lines-data-dynamic-status" "status" ON line."linesDataId" = status."referenceId" LEFT JOIN (SELECT status."leadTime" as "leadTime", MAX(timestamp) as max_timestamp FROM "IBF-app"."lines-data-dynamic-status" "status" LEFT JOIN "IBF-app"."lines-data" "line" ON line."linesDataId" = status."referenceId" WHERE line."linesDataCategory" = 'buildings' GROUP BY status."leadTime") "max_timestamp" ON status."leadTime" = max_timestamp."leadTime" WHERE line."linesDataCategory" = 'buildings' AND ("status"."timestamp" = max_timestamp.max_timestamp OR "status"."timestamp" IS NULL)`, + ); + await queryRunner.query( + `INSERT INTO "IBF-app"."typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, + [ + 'IBF-app', + 'VIEW', + 'buildings_exposure_per_lead_time', + 'SELECT line."referenceId",line.geom, status."leadTime", COALESCE("status"."exposed",FALSE) as "exposed" FROM "IBF-app"."lines-data" "line" LEFT JOIN "IBF-app"."lines-data-dynamic-status" "status" ON line."linesDataId" = status."referenceId" LEFT JOIN (SELECT status."leadTime" as "leadTime", MAX(timestamp) as max_timestamp FROM "IBF-app"."lines-data-dynamic-status" "status" LEFT JOIN "IBF-app"."lines-data" "line" ON line."linesDataId" = status."referenceId" WHERE line."linesDataCategory" = \'buildings\' GROUP BY status."leadTime") "max_timestamp" ON status."leadTime" = max_timestamp."leadTime" WHERE line."linesDataCategory" = \'buildings\' AND ("status"."timestamp" = max_timestamp.max_timestamp OR "status"."timestamp" IS NULL)', + ], + ); + await queryRunner.query( + `CREATE VIEW "IBF-app"."roads_exposure_per_lead_time" AS SELECT line."referenceId",line.geom,line.attributes->>'highway' as "highway", status."leadTime", COALESCE("status"."exposed",FALSE) as "exposed" FROM "IBF-app"."lines-data" "line" LEFT JOIN "IBF-app"."lines-data-dynamic-status" "status" ON line."linesDataId" = status."referenceId" LEFT JOIN (SELECT status."leadTime" as "leadTime", MAX(timestamp) as max_timestamp FROM "IBF-app"."lines-data-dynamic-status" "status" LEFT JOIN "IBF-app"."lines-data" "line" ON line."linesDataId" = status."referenceId" WHERE line."linesDataCategory" = 'roads' GROUP BY status."leadTime") "max_timestamp" ON status."leadTime" = max_timestamp."leadTime" WHERE line."linesDataCategory" = 'roads' AND ("status"."timestamp" = max_timestamp.max_timestamp OR "status"."timestamp" IS NULL)`, + ); + await queryRunner.query( + `INSERT INTO "IBF-app"."typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES (DEFAULT, $1, DEFAULT, $2, $3, $4)`, + [ + 'IBF-app', + 'VIEW', + 'roads_exposure_per_lead_time', + 'SELECT line."referenceId",line.geom,line.attributes->>\'highway\' as "highway", status."leadTime", COALESCE("status"."exposed",FALSE) as "exposed" FROM "IBF-app"."lines-data" "line" LEFT JOIN "IBF-app"."lines-data-dynamic-status" "status" ON line."linesDataId" = status."referenceId" LEFT JOIN (SELECT status."leadTime" as "leadTime", MAX(timestamp) as max_timestamp FROM "IBF-app"."lines-data-dynamic-status" "status" LEFT JOIN "IBF-app"."lines-data" "line" ON line."linesDataId" = status."referenceId" WHERE line."linesDataCategory" = \'roads\' GROUP BY status."leadTime") "max_timestamp" ON status."leadTime" = max_timestamp."leadTime" WHERE line."linesDataCategory" = \'roads\' AND ("status"."timestamp" = max_timestamp.max_timestamp OR "status"."timestamp" IS NULL)', + ], + ); + + await queryRunner.query( + `ALTER TABLE "IBF-app"."lines-data" DROP COLUMN "active"`, + ); + await queryRunner.query( + `ALTER TABLE "IBF-app"."point-data" DROP COLUMN "active"`, + ); + } +} diff --git a/services/API-service/src/api/lines-data/lines-data-views.entity.ts b/services/API-service/src/api/lines-data/lines-data-views.entity.ts index 7b82e0a23..0dc58c3b3 100644 --- a/services/API-service/src/api/lines-data/lines-data-views.entity.ts +++ b/services/API-service/src/api/lines-data/lines-data-views.entity.ts @@ -40,6 +40,7 @@ const getViewQuery = (type: LinesDataEnum) => { 'status."leadTime" = max_timestamp."leadTime"', ) .where(`line."linesDataCategory" = '${type}'`) + .andWhere('line.active = true') .andWhere( '(status.timestamp = max_timestamp.max_timestamp OR status.timestamp IS NULL)', ) diff --git a/services/API-service/src/api/lines-data/lines-data.entity.ts b/services/API-service/src/api/lines-data/lines-data.entity.ts index 66ffbf8ea..94eff67e7 100644 --- a/services/API-service/src/api/lines-data/lines-data.entity.ts +++ b/services/API-service/src/api/lines-data/lines-data.entity.ts @@ -35,4 +35,7 @@ export class LinesDataEntity { nullable: true, }) public geom: Geometry; + + @Column({ default: true }) + public active: boolean; } diff --git a/services/API-service/src/api/lines-data/lines-data.service.ts b/services/API-service/src/api/lines-data/lines-data.service.ts index d468d0720..bd84e1b3b 100644 --- a/services/API-service/src/api/lines-data/lines-data.service.ts +++ b/services/API-service/src/api/lines-data/lines-data.service.ts @@ -38,14 +38,17 @@ export class LinesDataService { countryCodeISO3: string, // eslint-disable-next-line @typescript-eslint/no-explicit-any validatedObjArray: any, - deleteExisting = true, + deactivateExisting = true, ) { // Delete existing entries - if (deleteExisting) { - await this.linesDataRepository.delete({ - countryCodeISO3: countryCodeISO3, - linesDataCategory: linesDataCategory, - }); + if (deactivateExisting) { + await this.linesDataRepository.update( + { + countryCodeISO3: countryCodeISO3, + linesDataCategory: linesDataCategory, + }, + { active: false }, + ); } const dataArray = validatedObjArray.map((line) => { @@ -56,6 +59,7 @@ export class LinesDataService { referenceId: line.fid || null, linesDataCategory: linesDataCategory, attributes: JSON.parse(JSON.stringify(pointAttributes)), + active: true, geom: (): string => `st_geomfromtext( 'GEOMETRYCOLLECTION(${line.wkt})')`, }; diff --git a/services/API-service/src/api/point-data/point-data.entity.ts b/services/API-service/src/api/point-data/point-data.entity.ts index cf0bb940c..ee2c4bf0c 100644 --- a/services/API-service/src/api/point-data/point-data.entity.ts +++ b/services/API-service/src/api/point-data/point-data.entity.ts @@ -34,6 +34,9 @@ export class PointDataEntity { @Column('json', { nullable: true }) public geom: JSON; + @Column({ default: true }) + public active: boolean; + @OneToMany( (): typeof DynamicPointDataEntity => DynamicPointDataEntity, (dynamicData): PointDataEntity => dynamicData.point, diff --git a/services/API-service/src/api/point-data/point-data.service.ts b/services/API-service/src/api/point-data/point-data.service.ts index f447c5cd2..18a57f24c 100644 --- a/services/API-service/src/api/point-data/point-data.service.ts +++ b/services/API-service/src/api/point-data/point-data.service.ts @@ -90,6 +90,7 @@ export class PointDataService { .where({ pointDataCategory: pointDataCategory, countryCodeISO3: countryCodeISO3, + active: true, }) .leftJoin( (subquery) => { @@ -150,14 +151,17 @@ export class PointDataService { countryCodeISO3: string, // eslint-disable-next-line @typescript-eslint/no-explicit-any validatedObjArray: any, - deleteExisting = true, + deactivateExisting = true, ) { - // Delete existing entries - if (deleteExisting) { - await this.pointDataRepository.delete({ - countryCodeISO3: countryCodeISO3, - pointDataCategory: pointDataCategory, - }); + // Deactivate existing entries + if (deactivateExisting) { + await this.pointDataRepository.update( + { + countryCodeISO3: countryCodeISO3, + pointDataCategory: pointDataCategory, + }, + { active: false }, + ); } const dataArray = validatedObjArray.map((point) => { @@ -169,6 +173,7 @@ export class PointDataService { referenceId: point.fid || null, pointDataCategory: pointDataCategory, attributes: JSON.parse(JSON.stringify(pointAttributes)), + active: true, geom: (): string => `st_asgeojson(st_MakePoint(${point.lon}, ${point.lat}))::json`, };