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

refactor: use leadTime enum instead of entity #1938

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from 'src/app/analytics/analytics.enum';
import { AnalyticsService } from 'src/app/analytics/analytics.service';
import { LayerControlInfoPopoverComponent } from 'src/app/components/layer-control-info-popover/layer-control-info-popover.component';
import { AREAS_OF_FOCUS } from 'src/app/models/area-of-focus.const';
import {
Country,
CountryDisasterSettings,
Expand Down Expand Up @@ -40,7 +41,7 @@ export class AreasOfFocusSummaryComponent implements OnInit, OnDestroy {
public country: Country;
public disasterType: DisasterType;
public countryDisasterSettings: CountryDisasterSettings;
public areasOfFocus: AreaOfFocus[];
public areasOfFocus: AreaOfFocus[] = AREAS_OF_FOCUS;
public triggeredAreas: TriggeredArea[];
public trigger: boolean;
public eventState: EventState;
Expand Down Expand Up @@ -144,21 +145,12 @@ export class AreasOfFocusSummaryComponent implements OnInit, OnDestroy {
triggeredAreas.forEach(this.onEachTriggeredArea(areaOfFocus));
};

const onAreasOfFocusChange = (areasOfFocus: AreaOfFocus[]) => {
this.areasOfFocus = areasOfFocus;

// Start calculation only when last area has eapActions attached to it
if (triggeredAreas[triggeredAreas.length - 1]?.eapActions) {
// For each area of focus ..
this.areasOfFocus.forEach(onEachAreaOfFocus);
}
this.changeDetectorRef.detectChanges();
};

// Get areas of focus from db
if (triggeredAreas.length) {
this.apiService.getAreasOfFocus().subscribe(onAreasOfFocusChange);
// Start calculation only when last area has eapActions attached to it
if (triggeredAreas[triggeredAreas.length - 1]?.eapActions) {
// For each area of focus ..
this.areasOfFocus.forEach(onEachAreaOfFocus);
}
this.changeDetectorRef.detectChanges();
}

private onEventStateChange = (eventState: EventState) => {
Expand Down
54 changes: 54 additions & 0 deletions interfaces/IBF-dashboard/src/app/models/area-of-focus.const.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { AreaOfFocus } from 'src/app/types/area-of-focus';

// ##TODO: exact same const as in back-end. Solve differently (but better than via endpoint?)
export const AREAS_OF_FOCUS: AreaOfFocus[] = [
{
id: 'drr',
label: 'Disaster Risk Reduction',
description:
'(DRR) is a systematic approach to identifying, assessing and reducing the risks of disaster.<br><br>It aims to reduce socio-economic vulnerabilities to disaster as well as dealing with the environmental and other hazards that trigger them.',
icon: 'Disaster risk reduction.svg',
},
{
id: 'shelter',
label: 'Shelter',
description:
'Shelter and Non-Food Items includes provision of shelter materials and non-food household item packages.<br><br>The theme also covers Camp Coordination and Camp Management.<br><br>Long-term/permanent reconstruction/rebuilding of housing is not covered by this area of focus.',
icon: 'Shelter.svg',
},
{
id: 'livelihood',
label: 'Livelihoods & Basic Needs',
description:
"Disasters can take a devastating toll on people's food security livelihoods and their basic needs.<br><br>They can increase people's socio-economic vulnerability and seriously impact their ability to recover, which in turn affects their ability to cope with future shocks and stresses.<br><br>These actions are aimed at protecting these needs as preparation for forecasted hazards.",
icon: 'Livelihood.svg',
},
{
id: 'health',
label: 'Health',
description:
'Health includes emergency medical services, equipment and supplies; reproductive health; psycho-social support; mobile medical clinics; and disease control and surveillance.',
icon: 'Health.svg',
},
{
id: 'wash',
label: 'WASH',
description:
'Water, Sanitation, and Hygiene includes emergency provision of safe drinking water, hygiene and sanitation services, environmental sanitation and water supply, as well as hygiene promotion campaigns.',
icon: 'Water-Sanitation-and-Hygiene.svg',
},
{
id: 'inclusion',
label: 'Protection, Gender and Inclusion',
description:
"People affected by disasters can have very different experiences.<br>A person's sex, gender identity, age, physical ability, race, nationality and many other factors can influence how they are vulnerable to, and affected by disasters, conflicts and crises. They can also affect how they respond and recover.<br><br>Emergencies can also make existing inequalities worse. This can be seen in the increase in incidences of sexual and gender-based violence (SGBV), violence against children and trafficking in human beings during and after emergencies.<br><ul><li><strong>Protection</strong> - addressing violence and keeping people safe from harm</li><li><strong>Gender and diversity</strong> - addressing discrimination and understanding people's different needs, risks and capacities</li><li><strong>Inclusion</strong> - actively addressing exclusion by meaningfully involving and engaging excluded people in our work</li></ul>",
icon: 'Gender.svg',
},
{
id: 'migration',
label: 'Migration',
description:
'Migration and displacement pose some of the biggest humanitarian challenges of our time.<br><br>These actions are aimed at supporting people on the move with saving lives and preventing suffering, help people cope with the risks and challenges of migration and work to protect and restore their dignity.',
icon: 'Internally-displaced.svg',
},
];
22 changes: 3 additions & 19 deletions interfaces/IBF-dashboard/src/app/services/api.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { tap } from 'rxjs/operators';
import { DEBUG_LOG } from 'src/app/config';
import { Country, DisasterType } from 'src/app/models/country.model';
import { CountryTriggers } from 'src/app/models/country-triggers.model';
Expand Down Expand Up @@ -154,19 +154,7 @@ export class ApiService {
if (minimalInfo) {
params = params.append('minimalInfo', String(minimalInfo));
}
return this.get(path, false, params).pipe(
map((countries) => {
return countries.map((country) => {
country.countryDisasterSettings?.map((disaster) => {
disaster.activeLeadTimes = disaster.activeLeadTimes.map(
(leadTime) => leadTime.leadTimeName,
);
return disaster;
});
return country;
});
}),
);
return this.get(path, false, params);
}

getTyphoonTrack(
Expand Down Expand Up @@ -291,7 +279,7 @@ export class ApiService {
adminLevel: number,
leadTime: string,
eventName: string,
): Observable<TriggeredArea> {
): Observable<TriggeredArea[]> {
let params = new HttpParams();
if (eventName) {
params = params.append('eventName', eventName);
Expand Down Expand Up @@ -367,10 +355,6 @@ export class ApiService {
);
}

getAreasOfFocus() {
return this.get('eap-actions/areas-of-focus', false);
}

checkEapAction(
action: string,
countryCodeISO3: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable } from '@angular/core';
import { DateTime } from 'luxon';
import { BehaviorSubject, Observable } from 'rxjs';
import { AREAS_OF_FOCUS } from 'src/app/models/area-of-focus.const';
import {
Country,
CountryDisasterSettings,
Expand Down Expand Up @@ -134,7 +135,7 @@ export class EapActionsService {
}
}

private onTriggeredAreas = (triggeredAreas) => {
private onTriggeredAreas = (triggeredAreas: TriggeredArea[]) => {
this.triggeredAreas = triggeredAreas;
this.triggeredAreas.sort((a, b) => {
if (a.triggerValue === b.triggerValue) {
Expand All @@ -149,6 +150,9 @@ export class EapActionsService {
this.mapTriggerValueToAlertClass(area);
this.filterEapActionsByMonth(area);
area.eapActions.forEach((action) => {
action.aofLabel = AREAS_OF_FOCUS.find(
(aof) => aof.id === action.aof,
).label;
if (Object.keys(action.month).length) {
Object.defineProperty(action, 'monthLong', {
value: {},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class RemoveLeadTimeEntity1737713818319 implements MigrationInterface {
name = 'RemoveLeadTimeEntity1737713818319';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "IBF-app"."admin-area-dynamic-data" DROP CONSTRAINT "FK_cb6c2fc0f20bcf164afc3e0301b"`,
);
await queryRunner.query(
`ALTER TABLE "IBF-app"."lines-data-dynamic-status" DROP CONSTRAINT "FK_2456d6fa601344982bacf47f6b9"`,
);
await queryRunner.query(
`ALTER TABLE "IBF-app"."dynamic-point-data" DROP CONSTRAINT "FK_289a1f52e25e270d9a28bd9d35a"`,
);
await queryRunner.query(
`ALTER TABLE "IBF-app"."typhoon-track" DROP CONSTRAINT "FK_7911edcf74dd1da5905dbc7e44e"`,
);
await queryRunner.query(
`ALTER TABLE "IBF-app"."country-disaster-settings" ADD "activeLeadTimes" json`,
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "IBF-app"."country-disaster-settings" DROP COLUMN "activeLeadTimes"`,
);
await queryRunner.query(
`ALTER TABLE "IBF-app"."typhoon-track" ADD CONSTRAINT "FK_7911edcf74dd1da5905dbc7e44e" FOREIGN KEY ("leadTime") REFERENCES "IBF-app"."lead-time"("leadTimeName") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "IBF-app"."dynamic-point-data" ADD CONSTRAINT "FK_289a1f52e25e270d9a28bd9d35a" FOREIGN KEY ("leadTime") REFERENCES "IBF-app"."lead-time"("leadTimeName") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "IBF-app"."lines-data-dynamic-status" ADD CONSTRAINT "FK_2456d6fa601344982bacf47f6b9" FOREIGN KEY ("leadTime") REFERENCES "IBF-app"."lead-time"("leadTimeName") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "IBF-app"."admin-area-dynamic-data" ADD CONSTRAINT "FK_cb6c2fc0f20bcf164afc3e0301b" FOREIGN KEY ("leadTime") REFERENCES "IBF-app"."lead-time"("leadTimeName") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class RemoveAreaOfFocusEntity1737716825902
implements MigrationInterface
{
name = 'RemoveAreaOfFocusEntity1737716825902';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "IBF-app"."eap-action" DROP CONSTRAINT "FK_f3ce61c374e05d7b8b05b8485d2"`,
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "IBF-app"."eap-action" ADD CONSTRAINT "FK_f3ce61c374e05d7b8b05b8485d2" FOREIGN KEY ("areaOfFocusId") REFERENCES "IBF-app"."area-of-focus"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {

import { CountryEntity } from '../country/country.entity';
import { DisasterEntity } from '../disaster/disaster.entity';
import { LeadTimeEntity } from '../lead-time/lead-time.entity';
import { DynamicIndicator } from './enum/dynamic-data-unit';
import { LeadTime } from './enum/lead-time.enum';

@Entity('admin-area-dynamic-data')
export class AdminAreaDynamicDataEntity {
Expand Down Expand Up @@ -53,7 +53,6 @@ export class AdminAreaDynamicDataEntity {
@Column({ nullable: true, type: 'real' })
public value: number;

@ManyToOne((): typeof LeadTimeEntity => LeadTimeEntity)
@JoinColumn({ name: 'leadTime', referencedColumnName: 'leadTimeName' })
public leadTime: string;
@Column({ nullable: true })
public leadTime: LeadTime;
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class UploadAdminAreaDynamicDataDto {
@ApiProperty({ example: LeadTime.month0 })
@IsNotEmpty()
@IsString()
@IsEnum(LeadTime)
public leadTime: LeadTime;

@ApiProperty({ example: DynamicIndicator.populationAffected })
Expand Down
12 changes: 3 additions & 9 deletions services/API-service/src/api/country/country-disaster.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import {
Column,
Entity,
JoinColumn,
JoinTable,
ManyToMany,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';

import { LeadTime } from '../admin-area-dynamic-data/enum/lead-time.enum';
import { DisasterType } from '../disaster/disaster-type.enum';
import { DisasterEntity } from '../disaster/disaster.entity';
import { LeadTimeEntity } from '../lead-time/lead-time.entity';
import { AdminLevel } from './admin-level.enum';
import { CountryEntity } from './country.entity';

Expand Down Expand Up @@ -48,12 +46,8 @@ export class CountryDisasterSettingsEntity {
public defaultAdminLevel: AdminLevel;

@ApiProperty()
@ManyToMany(
(): typeof LeadTimeEntity => LeadTimeEntity,
(leadTime): CountryDisasterSettingsEntity[] => leadTime.countries,
)
@JoinTable()
public activeLeadTimes: LeadTimeEntity[];
@Column('json', { nullable: true })
public activeLeadTimes: LeadTime[];

@ApiProperty({ example: [3, 10] })
@Column('json', { nullable: true })
Expand Down
2 changes: 0 additions & 2 deletions services/API-service/src/api/country/country.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

import { DisasterEntity } from '../disaster/disaster.entity';
import { LeadTimeEntity } from '../lead-time/lead-time.entity';
import { NotificationInfoEntity } from '../notification/notifcation-info.entity';
import { UserModule } from '../user/user.module';
import { CountryDisasterSettingsEntity } from './country-disaster.entity';
Expand All @@ -19,7 +18,6 @@ import { CountryService } from './country.service';
CountryEntity,
DisasterEntity,
CountryDisasterSettingsEntity,
LeadTimeEntity,
NotificationInfoEntity,
]),
],
Expand Down
20 changes: 1 addition & 19 deletions services/API-service/src/api/country/country.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { In, Repository } from 'typeorm';
import countries from '../../scripts/json/countries.json';
import notificationInfos from '../../scripts/json/notification-info.json';
import { DisasterEntity } from '../disaster/disaster.entity';
import { LeadTimeEntity } from '../lead-time/lead-time.entity';
import { NotificationInfoEntity } from '../notification/notifcation-info.entity';
import { CountryDisasterSettingsEntity } from './country-disaster.entity';
import { CountryEntity } from './country.entity';
Expand All @@ -19,12 +18,10 @@ describe('CountryService', () => {
let countryRepository: Repository<CountryEntity>;
let disasterRepository: Repository<DisasterEntity>;
let countryDisasterSettingsRepository: Repository<CountryDisasterSettingsEntity>;
let leadTimeRepository: Repository<LeadTimeEntity>;
let notificationInfoRepository: Repository<NotificationInfoEntity>;

const relations = [
'countryDisasterSettings',
'countryDisasterSettings.activeLeadTimes',
'disasterTypes',
'notificationInfo',
];
Expand All @@ -45,10 +42,6 @@ describe('CountryService', () => {
provide: getRepositoryToken(CountryDisasterSettingsEntity),
useClass: Repository,
},
{
provide: getRepositoryToken(LeadTimeEntity),
useClass: Repository,
},
{
provide: getRepositoryToken(NotificationInfoEntity),
useClass: Repository,
Expand All @@ -66,9 +59,6 @@ describe('CountryService', () => {
countryDisasterSettingsRepository = module.get<
Repository<CountryDisasterSettingsEntity>
>(getRepositoryToken(CountryDisasterSettingsEntity));
leadTimeRepository = module.get<Repository<LeadTimeEntity>>(
getRepositoryToken(LeadTimeEntity),
);
notificationInfoRepository = module.get<Repository<NotificationInfoEntity>>(
getRepositoryToken(NotificationInfoEntity),
);
Expand Down Expand Up @@ -114,9 +104,6 @@ describe('CountryService', () => {
jest
.spyOn(countryRepository, 'save')
.mockResolvedValue(new CountryEntity());
jest
.spyOn(leadTimeRepository, 'find')
.mockResolvedValue([new LeadTimeEntity()]);
jest
.spyOn(countryDisasterSettingsRepository, 'save')
.mockResolvedValue(new CountryDisasterSettingsEntity());
Expand All @@ -140,12 +127,7 @@ describe('CountryService', () => {
});
expect(countryRepository.save).toHaveBeenCalled();

for (const countryDisasterSetting of country.countryDisasterSettings as CountryDisasterSettingsDto[]) {
expect(leadTimeRepository.find).toHaveBeenCalledWith({
where: countryDisasterSetting.activeLeadTimes.map(
(leadTimeName) => ({ leadTimeName }),
),
});
for (const _countryDisasterSetting of country.countryDisasterSettings as CountryDisasterSettingsDto[]) {
expect(countryDisasterSettingsRepository.save).toHaveBeenCalled();
expect(
countryDisasterSettingsRepository.findOne,
Expand Down
Loading
Loading