Skip to content

Commit

Permalink
add admin region of production to new geocoding approach
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeh committed Jan 11, 2025
1 parent bed9455 commit abc1c4a
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 60 deletions.
4 changes: 2 additions & 2 deletions api/src/modules/geo-coding/geo-coding.service-v2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,12 @@ export class GeoCodingServiceV2 {
private loadStrategies(repository: GeocodingRepository): void {
this.strategies = {
[LOCATION_TYPES.UNKNOWN]: new UnknownLocationGeoCodingStrategy(
this.manager,
repository,
),
[LOCATION_TYPES.POINT_OF_PRODUCTION]:
new PointOfProductionGeocodingStrategy(repository, this.geocoder),
[LOCATION_TYPES.COUNTRY_OF_PRODUCTION]:
new CountryOfProductionGeoCodingStrategy(this.manager),
new CountryOfProductionGeoCodingStrategy(repository),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,39 @@ import { BaseStrategy } from 'modules/geo-coding/strategies/base-strategy';
import { SourcingData } from 'modules/import-data/sourcing-data/dto-processor.service';
import { GeoCodingError } from 'modules/geo-coding/errors/geo-coding.error';
import { AdminRegion } from 'modules/admin-regions/admin-region.entity';
import { IGeoCodingStrategy } from './geo-coding.strategy.interface';
import {
GeoCodedLocation,
SourcingLocationInfo,
} from '../geo-coding.service-v2';
import { GeocodingRepository } from './geocoding.repository';

@Injectable()
export class AdminRegionOfProductionService extends BaseStrategy {
async geoCodeAdministrativeRegionOfProduction(
sourcingData: SourcingData,
): Promise<SourcingData> {
export class AdminRegionOfProductionService implements IGeoCodingStrategy {
repo: GeocodingRepository;

constructor(geocodingRepository: GeocodingRepository) {
this.repo = geocodingRepository;
}

async geoCodeLocation(
locationInfo: SourcingLocationInfo,
): Promise<GeoCodedLocation> {
// TODO: Since this has become required for all location types, we should validate this at DTO level
if (!sourcingData.locationCountryInput) {
if (!locationInfo.locationCountryInput) {
throw new GeoCodingError(
`A Country must be provided for Administrative Region Of Production location type`,
);
}
if (!sourcingData.locationAdminRegionInput) {
if (!locationInfo.locationAdminRegionInput) {
throw new GeoCodingError(
`An Admin Region must be provided for Administrative Region Of Production location type`,
);
}
if (
sourcingData.locationAddressInput ||
sourcingData.locationLatitude ||
sourcingData.locationLongitude
locationInfo.locationAddressInput ||
locationInfo.locationLatitude ||
locationInfo.locationLongitude
) {
throw new GeoCodingError(
`Address and Coordinates should be empty for Administrative Region of Production location type`,
Expand All @@ -34,35 +46,35 @@ export class AdminRegionOfProductionService extends BaseStrategy {

// Ilhas de Martim Vaz does not exist in GADM, switching to Ishla Trindade

const parent: { adminRegionId: string; geoRegionId: string } =
await this.adminRegionService.getAdminRegionAndGeoRegionIdsByAdminRegionName(
sourcingData.locationCountryInput,
{ level: 0 },
const { adminRegion: parentAdminRegion, geoRegion } =
await this.repo.getCountryAdminRegionAndGeoRegionByCountryName(
locationInfo.locationCountryInput,
);

const descendants: AdminRegion[] =
await this.adminRegionService.getAdminRegionDescendants(
[parent.adminRegionId],
{ fullEntities: true },
);
const adminRegionRepository =
this.repo.manager.getTreeRepository(AdminRegion);

const descendants = await adminRegionRepository.manager
.getTreeRepository(AdminRegion)
.findDescendants(parentAdminRegion);

const location: AdminRegion | undefined = descendants.find(
(regions: AdminRegion) =>
regions.name === sourcingData.locationAdminRegionInput,
regions.name === locationInfo.locationAdminRegionInput,
);
if (!location) {
throw new GeoCodingError(
`Admin Region of Production: ${sourcingData.locationAdminRegionInput} is not part of Country: ${sourcingData.locationCountryInput}`,
`Admin Region of Production: ${locationInfo.locationAdminRegionInput} is not part of Country: ${locationInfo.locationCountryInput}`,
);
}

const { id: adminRegionId, geoRegionId } =
await this.adminRegionService.getAdminRegionById(location.id);
const adminRegion = await adminRegionRepository.findOneOrFail({
where: { id: location.id },
});

return {
...sourcingData,
adminRegionId,
geoRegionId,
adminRegion,
geoRegion,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ import {
SourcingLocationInfo,
} from '../geo-coding.service-v2';
import { EntityManager } from 'typeorm';
import { AdminRegion } from 'modules/admin-regions/admin-region.entity';
import { GeocodingRepository } from './geocoding.repository';

export class CountryOfProductionGeoCodingStrategy
implements IGeoCodingStrategy
{
manager: EntityManager;
repo: GeocodingRepository;

constructor(manager: EntityManager) {
this.manager = manager;
constructor(geocodingRepository: GeocodingRepository) {
this.repo = geocodingRepository;
}

async geoCodeLocation(
Expand All @@ -32,7 +33,7 @@ export class CountryOfProductionGeoCodingStrategy
);

const { adminRegion, geoRegion } =
await this.getAdminRegionAndGeoRegionByAdminRegionName(
await this.repo.getCountryAdminRegionAndGeoRegionByCountryName(
locationInfo.locationCountryInput,
);

Expand All @@ -41,26 +42,4 @@ export class CountryOfProductionGeoCodingStrategy
geoRegion,
};
}

async getAdminRegionAndGeoRegionByAdminRegionName(
adminRegionName: string,
): Promise<GeoCodedLocation> {
const queryBuilder = this.manager
.createQueryBuilder(AdminRegion, 'adminRegion')
.innerJoinAndSelect('adminRegion.geoRegion', 'geoRegion')
.where('adminRegion.name = :adminRegionName', { adminRegionName })
.andWhere('adminRegion.level = :level', { level: 0 }); // For country of production, we need to get the country, which is level 0

const adminRegion = await queryBuilder.getOne();

if (!adminRegion || !adminRegion.geoRegion) {
throw new NotFoundException(
`An Country level Admin Region with name ${adminRegionName} could not be found`,
);
}
return {
adminRegion,
geoRegion: adminRegion.geoRegion,
};
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
import { NotFoundException } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import { IGeoCodingStrategy } from './geo-coding.strategy.interface';
import { AdminRegion } from 'modules/admin-regions/admin-region.entity';
import { IGeoCodingStrategy } from 'modules/geo-coding/strategies_v2/geo-coding.strategy.interface';

import {
GeoCodedLocation,
SourcingLocationInfo,
} from '../geo-coding.service-v2';
import { GeocodingRepository } from './geocoding.repository';
} from 'modules/geo-coding/geo-coding.service-v2';
import { GeocodingRepository } from 'modules/geo-coding/strategies_v2/geocoding.repository';

export class UnknownLocationGeoCodingStrategy implements IGeoCodingStrategy {
manager: EntityManager;
repo: GeocodingRepository;

constructor(manager: EntityManager) {
this.manager = manager;
constructor(geocodingRepository: GeocodingRepository) {
this.repo = geocodingRepository;
}

async geoCodeLocation(
Expand Down

0 comments on commit abc1c4a

Please sign in to comment.