Backup before syncing server scripts

This commit is contained in:
Hamza-Ayed
2026-08-16 13:17:56 +03:00
parent dc9d546733
commit 32865e78df
26 changed files with 3919 additions and 2123 deletions
@@ -63,8 +63,12 @@ export class AdministrativeLinkingService {
// Determine country from BBox if not provided (simple heuristic)
let detectedCountry = country;
if (!detectedCountry) {
const firstLat = parseFloat(bbox.split(',')[0]);
if (firstLat > 32.3) detectedCountry = 'syria';
const parts = bbox.split(',').map(parseFloat);
const firstLat = parts[0];
const lngs = [parts[1], parts[3]].filter(n => !isNaN(n));
const maxLng = lngs.length ? Math.max(...lngs) : undefined;
if (maxLng !== undefined && maxLng > 42.5) detectedCountry = 'iraq';
else if (firstLat > 32.3) detectedCountry = 'syria';
else if (firstLat < 31.0) detectedCountry = 'egypt';
else detectedCountry = 'jordan';
}
@@ -98,8 +102,13 @@ export class AdministrativeLinkingService {
LIMIT 1
),
(
-- حدّ 50كم إلزامي: بدونه تُسنَد نقطة لا تقع داخل أي حدود إلى أقرب
-- مقاطعة في الجدول كله مهما بَعُدت. حين استوردنا العراق قبل حدوده
-- الإدارية، رُبطت أحياء بغداد الـ185 بمقاطعات سورية/أردنية على بعد
-- ~700كم. عدم الربط أفضل من ربط خاطئ صامت.
SELECT id FROM admin_boundaries ab
WHERE ab.admin_level IN (6, 8)
AND ST_DWithin(ab.geom::geography, np.geometry::geography, 50000)
ORDER BY ab.geom::geometry <-> np.geometry::geometry
LIMIT 1
)
@@ -209,9 +218,9 @@ export class AdministrativeLinkingService {
}
/**
* Step 3: Link all Places (Jordan, Syria, Egypt) to the full administrative hierarchy
* Step 3: Link all Places (Jordan, Syria, Egypt, Iraq) to the full administrative hierarchy
*/
async linkPlaces(country: 'jordan' | 'syria' | 'egypt') {
async linkPlaces(country: 'jordan' | 'syria' | 'egypt' | 'iraq') {
const tableName = `places_${country}`;
// Pre-flight diagnostics
@@ -28,7 +28,7 @@ export class SearchQueryDto {
radius?: number = 20000;
@IsOptional()
@IsEnum(['jordan', 'syria', 'egypt'])
@ApiPropertyOptional({ description: 'Country filter', enum: ['jordan', 'syria', 'egypt'] })
@IsEnum(['jordan', 'syria', 'egypt', 'iraq'])
@ApiPropertyOptional({ description: 'Country filter', enum: ['jordan', 'syria', 'egypt', 'iraq'] })
country?: string;
}
@@ -10,7 +10,8 @@ export enum CandidateStatus {
export enum CountryCode {
JORDAN = 'JORDAN',
SYRIA = 'SYRIA',
EGYPT = 'EGYPT'
EGYPT = 'EGYPT',
IRAQ = 'IRAQ'
}
@Entity('map_candidates')
@@ -0,0 +1,5 @@
import { Entity } from 'typeorm';
import { BasePlace } from './base-place.entity';
@Entity('places_iraq')
export class PlaceIraq extends BasePlace {}
@@ -20,7 +20,7 @@ export class GeocodingInitService implements OnModuleInit {
await this.repo.query('CREATE EXTENSION IF NOT EXISTS pg_trgm;');
// Migration: Rename old admin_level columns to descriptive names if they exist
const tables = ['places_jordan', 'places_syria', 'places_egypt'];
const tables = ['places_jordan', 'places_syria', 'places_egypt', 'places_iraq'];
const columnMapping = [
{ old: 'admin_level4_id', new: 'governorate_id' },
{ old: 'admin_level6_id', new: 'district_id' },
@@ -83,15 +83,24 @@ export class GeocodingInitService implements OnModuleInit {
FOR EACH ROW EXECUTE FUNCTION sync_place_location();
`);
// 4. GIST Geometry Indexes for Jordan and Egypt
await this.repo.query(`
DROP TRIGGER IF EXISTS trg_sync_place_location_iraq ON places_iraq;
CREATE TRIGGER trg_sync_place_location_iraq
BEFORE INSERT OR UPDATE ON places_iraq
FOR EACH ROW EXECUTE FUNCTION sync_place_location();
`);
// 4. GIST Geometry Indexes for Jordan, Egypt and Iraq
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_jordan_location ON places_jordan USING gist (location);');
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_egypt_location ON places_egypt USING gist (location);');
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_iraq_location ON places_iraq USING gist (location);');
// 5. GIST Trigram Indexes for Jordan and Egypt
// 5. GIST Trigram Indexes for Jordan, Egypt and Iraq
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_jordan_names_trgm ON places_jordan USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_egypt_names_trgm ON places_egypt USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_iraq_names_trgm ON places_iraq USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
this.logger.log('Geocoding database triggers and optimized indexes initialized for Syria, Jordan, and Egypt.');
this.logger.log('Geocoding database triggers and optimized indexes initialized for Syria, Jordan, Egypt, and Iraq.');
} catch (err) {
this.logger.error('Failed to initialize database geocoding triggers:', err);
}
@@ -61,7 +61,7 @@ export class GeocodingController {
@ApiOperation({ summary: 'Delete a place by name or ID' })
@ApiQuery({ name: 'name', required: false })
@ApiQuery({ name: 'id', required: false, type: Number })
@ApiQuery({ name: 'country', required: true, enum: ['jordan', 'syria', 'egypt'] })
@ApiQuery({ name: 'country', required: true, enum: ['jordan', 'syria', 'egypt', 'iraq'] })
async deletePlace(
@Query('country') country: string,
@Query('name') name?: string,
@@ -124,7 +124,7 @@ export class GeocodingController {
@UseGuards(AdminGuard)
@ApiOperation({ summary: 'Sync neighborhood points from OSM for a bbox' })
@ApiQuery({ name: 'bbox', required: false })
@ApiQuery({ name: 'country', required: false, enum: ['jordan', 'syria', 'egypt'] })
@ApiQuery({ name: 'country', required: false, enum: ['jordan', 'syria', 'egypt', 'iraq'] })
async syncNeighborhoods(@Query('bbox') bbox?: string, @Query('country') country?: string) {
return this.adminLinkingService.syncOsmNeighborhoodPoints(bbox, country);
}
@@ -132,7 +132,7 @@ export class GeocodingController {
@Post('admin/generate-voronoi')
@UseGuards(AdminGuard)
@ApiOperation({ summary: 'Generate Voronoi polygons for neighborhoods' })
@ApiQuery({ name: 'country', required: false, enum: ['jordan', 'syria', 'egypt'] })
@ApiQuery({ name: 'country', required: false, enum: ['jordan', 'syria', 'egypt', 'iraq'] })
async generateVoronoi(@Query('country') country?: string) {
return this.adminLinkingService.generateVoronoiNeighborhoods(country);
}
@@ -140,8 +140,8 @@ export class GeocodingController {
@Post('admin/link-places')
@UseGuards(AdminGuard)
@ApiOperation({ summary: 'Link places to administrative hierarchy' })
@ApiQuery({ name: 'country', required: true, enum: ['jordan', 'syria', 'egypt'] })
async linkPlaces(@Query('country') country: 'jordan' | 'syria' | 'egypt') {
@ApiQuery({ name: 'country', required: true, enum: ['jordan', 'syria', 'egypt', 'iraq'] })
async linkPlaces(@Query('country') country: 'jordan' | 'syria' | 'egypt' | 'iraq') {
return this.adminLinkingService.linkPlaces(country);
}
}
@@ -6,6 +6,7 @@ import { GeocodingController } from './geocoding.controller';
import { PlaceSyria } from './entities/place-syria.entity';
import { PlaceJordan } from './entities/place-jordan.entity';
import { PlaceEgypt } from './entities/place-egypt.entity';
import { PlaceIraq } from './entities/place-iraq.entity';
import { OsmArea } from './entities/osm-area.entity';
import { OsmPointWithArea } from './entities/osm-point-with-area.entity';
import { AdminBoundary } from './entities/admin-boundary.entity';
@@ -27,6 +28,7 @@ import { IndexRefreshService } from './index-refresh.service';
PlaceSyria,
PlaceJordan,
PlaceEgypt,
PlaceIraq,
OsmArea,
OsmPointWithArea,
AdminBoundary,
+161 -10
View File
@@ -6,6 +6,7 @@ import type { Cache } from 'cache-manager';
import { PlaceSyria } from './entities/place-syria.entity';
import { PlaceJordan } from './entities/place-jordan.entity';
import { PlaceEgypt } from './entities/place-egypt.entity';
import { PlaceIraq } from './entities/place-iraq.entity';
import { BasePlace } from './entities/base-place.entity';
import { OsmArea } from './entities/osm-area.entity';
import { OsmPointWithArea } from './entities/osm-point-with-area.entity';
@@ -23,6 +24,8 @@ export class GeocodingService {
private placesJordanRepository: Repository<PlaceJordan>,
@InjectRepository(PlaceEgypt)
private placesEgyptRepository: Repository<PlaceEgypt>,
@InjectRepository(PlaceIraq)
private placesIraqRepository: Repository<PlaceIraq>,
@InjectRepository(OsmArea)
private osmAreasRepository: Repository<OsmArea>,
@InjectRepository(OsmPointWithArea)
@@ -35,6 +38,11 @@ export class GeocodingService {
* تحديد المستودع المناسب بناءً على الإحداثيات الجغرافية
*/
private getRepositoryForCoords(lat: number, lng: number): Repository<BasePlace> {
// Iraq checked first: its western desert (Anbar) extends to lng ~38.7, which
// overlaps the Jordan/Syria box below (lng <= 42.5). Order matters here.
if (lat >= 29 && lat <= 37.5 && lng >= 38.7 && lng <= 48.8) {
return (this.placesIraqRepository as unknown) as Repository<BasePlace>;
}
if (lat >= 29 && lat <= 37.5 && lng >= 34.5 && lng <= 42.5) {
if (lat > 32.5 && lng > 35.8) return (this.placesSyriaRepository as unknown) as Repository<BasePlace>;
return (this.placesJordanRepository as unknown) as Repository<BasePlace>;
@@ -47,6 +55,9 @@ export class GeocodingService {
private identifyRegion(lat?: number, lng?: number): string | undefined {
if (lat === undefined || lng === undefined) return undefined;
if (lat >= 29 && lat <= 37.5 && lng >= 38.7 && lng <= 48.8) {
return 'iraq';
}
if (lat >= 29 && lat <= 37.5 && lng >= 34.5 && lng <= 42.5) {
if (lat > 32.5 && lng > 35.8) return 'syria';
return 'jordan';
@@ -60,6 +71,7 @@ export class GeocodingService {
private getTableNameForRepo(repo: Repository<BasePlace>): string {
if (repo === (this.placesJordanRepository as unknown)) return 'places_jordan';
if (repo === (this.placesEgyptRepository as unknown)) return 'places_egypt';
if (repo === (this.placesIraqRepository as unknown)) return 'places_iraq';
return 'places_syria';
}
@@ -98,7 +110,7 @@ export class GeocodingService {
}
let regionCondition = '';
if (targetRegion && ['syria', 'jordan', 'egypt'].includes(targetRegion)) {
if (targetRegion && ['syria', 'jordan', 'egypt', 'iraq'].includes(targetRegion)) {
regionCondition = `AND (region = '${targetRegion}' OR region = 'global')`;
}
@@ -251,6 +263,7 @@ export class GeocodingService {
private getRepoByTableName(tableName: string): Repository<any> {
if (tableName === 'places_egypt') return this.placesEgyptRepository;
if (tableName === 'places_iraq') return this.placesIraqRepository;
if (tableName === 'places_jordan') return this.placesJordanRepository;
return this.placesSyriaRepository;
}
@@ -296,7 +309,7 @@ export class GeocodingService {
let queryParams: any[] = [`${normalizedQuery}%`];
let regionCondition = '';
if (targetRegion && ['syria', 'jordan', 'egypt'].includes(targetRegion)) {
if (targetRegion && ['syria', 'jordan', 'egypt', 'iraq'].includes(targetRegion)) {
regionCondition = `AND (region = '${targetRegion}' OR region = 'global')`;
}
@@ -336,10 +349,62 @@ export class GeocodingService {
}
}
/**
* العنوان العراقي بنية رقمية: محافظة ← منطقة ← محلة ← زقاق ← دار.
* المحلة والزقاق هما ما يستعمله الناس فعلياً، لا اسم الشارع.
*
* في OSM يُخزَّن الزقاق كاسم الطريق نفسه بصيغة "647-19" (محلة-زقاق) داخل
* planet_osm_line — وهو الجدول الوحيد الذي يحملها، ولم يكن reverseGeocode
* يستعلمه إطلاقاً. لذا كان السائق يقف داخل زقاق 19 ولا نملك ما نسمّيه به.
*
* هندسة planet_osm_line بـ SRID 3857 (افتراضي osm2pgsql بلا -E)، لذا نحوّل
* نقطة البحث إليها ليعمل فهرس GIST؛ ثم نقيس المسافة الحقيقية على geography
* للفائز وحده — فمسافات 3857 منتفخة بنحو 19% عند خط عرض بغداد.
*/
private async findIraqiAddress(lat: number, lng: number) {
const MAX_DISTANCE_M = 150; // أبعد من ذلك لم يعد الزقاق وصفاً للموقع
try {
const rows = await this.osmPointsRepository.query(
`
WITH p AS (
SELECT ST_Transform(ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326), 3857) AS g3857,
ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326)::geography AS geog
)
SELECT l.name,
ST_Distance(ST_Transform(l.way, 4326)::geography, p.geog) AS distance
FROM planet_osm_line l, p
WHERE l.name ~ '^[0-9]{2,4}-[0-9]{1,3}$'
ORDER BY l.way <-> p.g3857
LIMIT 1
`,
[lng, lat],
);
const row = rows?.[0];
if (!row || Number(row.distance) > MAX_DISTANCE_M) return null;
const [mahalla, zuqaq] = String(row.name).split('-');
return {
mahalla,
zuqaq,
distance: Number(row.distance),
text: `محلة ${mahalla}، زقاق ${zuqaq}`,
};
} catch (e) {
this.logger.warn(`Iraqi address lookup failed: ${e.message}`);
return null;
}
}
async reverseGeocode(lat: number, lng: number) {
try {
const repo = this.getRepositoryForCoords(lat, lng);
const tableName = this.getTableNameForRepo(repo);
// نطلقه بالتوازي مع بقية الاستعلامات؛ العراق وحده يدفع تكلفته
const iraqiAddressPromise =
this.identifyRegion(lat, lng) === 'iraq'
? this.findIraqiAddress(lat, lng)
: Promise.resolve(null);
const queryPromises: Promise<any[]>[] = [];
queryPromises.push(repo.query(`
@@ -402,7 +467,10 @@ export class GeocodingService {
ORDER BY location <-> ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326) ASC LIMIT 5
`, [lng, lat]));
const results = await Promise.allSettled(queryPromises);
const [results, iraqiAddress] = await Promise.all([
Promise.allSettled(queryPromises),
iraqiAddressPromise,
]);
let allResults: any[] = [];
results.forEach(res => {
if (res.status === 'fulfilled' && res.value) allResults.push(...res.value);
@@ -413,7 +481,17 @@ export class GeocodingService {
.slice(0, 5)
.map(r => {
const distance = Number(r.distance);
const fullAddressParts = [r.name_ar || r.name, r.address, r.neighbourhood, r.district, r.governorate].filter(Boolean);
const ZUQAQ_NAME_RE = /^[0-9]{2,4}-[0-9]{1,3}$/;
const rawName = r.name_ar || r.name;
const nameIsZuqaq = typeof rawName === 'string' && ZUQAQ_NAME_RE.test(rawName);
const fullAddressParts = [
nameIsZuqaq ? null : rawName, // "605-9" كنتيجة بحث لا يضيف شيئاً فوق "زقاق 9"
r.address,
iraqiAddress?.text,
r.neighbourhood,
r.district,
r.governorate,
].filter(Boolean);
let humanReadable = r.name_ar || r.name;
if (distance <= 20) {
@@ -430,8 +508,18 @@ export class GeocodingService {
humanReadable = [streetPart, districtPart].filter(Boolean).join('، ');
}
// في العراق المحلة والزقاق هما العنوان الفعلي، فيتصدّران الوصف
// ويبقى المعلم القريب لاحقةً توضيحية — إلا إذا كان "المعلم" نفسه
// مجرد اسم زقاق آخر (مثل "605-9")، فتكرار الرقم لا يفيد أحداً.
if (iraqiAddress) {
const landmark = distance <= 70 && !nameIsZuqaq ? rawName : null;
humanReadable = [iraqiAddress.text, landmark].filter(Boolean).join(' — ');
}
return {
...r,
mahalla: iraqiAddress?.mahalla,
zuqaq: iraqiAddress?.zuqaq,
latitude: parseFloat(r.latitude),
longitude: parseFloat(r.longitude),
human_readable_address: humanReadable,
@@ -444,20 +532,50 @@ export class GeocodingService {
}
}
/**
* يقبل lat/lng أو latitude/longitude ويرفض ما ليس رقماً.
*
* سبب وجوده: العملاء يرسلون {lat, lng} (كما في POST /geocoding/places)، بينما
* addPlace كان يقرأ data.latitude فقط → Number(undefined) = NaN. عندها تفشل كل
* مقارنات getRepositoryForCoords فيسقط الاستدعاء على مستودع سوريا الافتراضي،
* فيظهر مكان أردني في دمشق بإحداثيات تالفة. الصمت هنا أسوأ من الخطأ.
*/
private extractCoords(data: any): { lat: number; lng: number } {
const lat = Number(data?.lat ?? data?.latitude);
const lng = Number(data?.lng ?? data?.longitude);
if (!Number.isFinite(lat) || !Number.isFinite(lng)) {
throw new HttpException(
'Invalid coordinates: provide numeric lat/lng (or latitude/longitude)',
HttpStatus.BAD_REQUEST,
);
}
if (lat < -90 || lat > 90 || lng < -180 || lng > 180) {
throw new HttpException(
`Coordinates out of range: lat=${lat}, lng=${lng}`,
HttpStatus.BAD_REQUEST,
);
}
return { lat, lng };
}
async addPlace(data: Partial<BasePlace>) {
try {
const lat = Number(data.latitude), lng = Number(data.longitude);
const { lat, lng } = this.extractCoords(data);
const repo = this.getRepositoryForCoords(lat, lng), tableName = this.getTableNameForRepo(repo);
const newPlace = repo.create({ ...data, latitude: lat, longitude: lng, created_at: new Date(), location: { type: 'Point', coordinates: [lng, lat] } });
const savedPlace = await repo.save(newPlace);
await repo.query(`UPDATE ${tableName} SET location = ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326) WHERE id = $3`, [lng, lat, savedPlace.id]);
return { ...savedPlace, latitude: lat, longitude: lng, location: `POINT(${lng} ${lat})` };
} catch (error) { throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR); }
} catch (error) {
// لا نبتلع أخطاء التحقق (400) ونحولها إلى 500
if (error instanceof HttpException) throw error;
throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async upsertPlace(data: Partial<BasePlace>) {
try {
const lat = Number(data.latitude), lng = Number(data.longitude);
const { lat, lng } = this.extractCoords(data);
const repo = this.getRepositoryForCoords(lat, lng), tableName = this.getTableNameForRepo(repo);
const existing = await repo.query(`SELECT id, name FROM ${tableName} WHERE ST_DistanceSphere(location, ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326)) < 15 LIMIT 1`, [lng, lat]);
if (existing && existing.length > 0) {
@@ -469,17 +587,48 @@ export class GeocodingService {
} catch (error) { throw error; }
}
/**
* الاستيراد الجملي (يستخدمه السكرابر). كان يبتلع كل خطأ بصمت
* (`catch (e) {}`) ويعيد عدد الناجحين فقط — فاستيراد 10 آلاف مكان يسقط منه
* 3 آلاف دون أثر ولا سبب. الآن نُعيد أول 50 خطأ مع رقم السطر.
*/
async upsertBatch(places: Partial<BasePlace>[]) {
const results: any[] = [];
for (const place of places) { try { const r = await this.addPlace(place); results.push({ id: r.id, action: 'created' }); } catch (e) {} }
return { total: places.length, processed: results.length, created: results.length, updated: 0 };
const errors: { index: number; name?: string; reason: string }[] = [];
for (const [index, place] of places.entries()) {
try {
const r = await this.addPlace(place);
results.push({ id: r.id, action: 'created' });
} catch (e) {
if (errors.length < 50) {
errors.push({
index,
name: (place as any)?.name,
reason: e instanceof HttpException ? e.message : (e?.message ?? 'unknown'),
});
}
}
}
const failed = places.length - results.length;
return {
total: places.length,
processed: results.length,
created: results.length,
updated: 0,
failed,
errors,
errorsTruncated: failed > errors.length,
};
}
async getRecentPlaces(limit: number = 50) {
const s = await this.placesSyriaRepository.find({ order: { created_at: 'DESC' }, take: limit });
const j = await this.placesJordanRepository.find({ order: { created_at: 'DESC' }, take: limit });
const e = await this.placesEgyptRepository.find({ order: { created_at: 'DESC' }, take: limit });
return [...s, ...j, ...e].sort((a, b) => b.created_at.getTime() - a.created_at.getTime()).slice(0, limit);
const i = await this.placesIraqRepository.find({ order: { created_at: 'DESC' }, take: limit });
return [...s, ...j, ...e, ...i].sort((a, b) => b.created_at.getTime() - a.created_at.getTime()).slice(0, limit);
}
async getAllPlacesGeoJSON() {
@@ -488,6 +637,7 @@ export class GeocodingService {
SELECT id::text, name_ar as name, category, latitude, longitude, address, 'user' as region FROM places_syria
UNION ALL SELECT id::text, name_ar as name, category, latitude, longitude, address, 'user' as region FROM places_jordan
UNION ALL SELECT id::text, name_ar as name, category, latitude, longitude, address, 'user' as region FROM places_egypt
UNION ALL SELECT id::text, name_ar as name, category, latitude, longitude, address, 'user' as region FROM places_iraq
UNION ALL SELECT id::text, COALESCE(names->>'primary', names->>'common', 'Building') as name, 'building' as category, ST_Y(ST_Centroid(location)) as latitude, ST_X(ST_Centroid(location)) as longitude, '' as address, 'overture' as region FROM overture_building WHERE names->>'primary' IS NOT NULL LIMIT 500
UNION ALL SELECT id::text, COALESCE(names->>'primary', names->>'common', 'Street') as name, 'street' as category, ST_Y(ST_Centroid(location)) as latitude, ST_X(ST_Centroid(location)) as longitude, '' as address, 'overture' as region FROM overture_segment WHERE names->>'primary' IS NOT NULL LIMIT 500
UNION ALL SELECT id::text, COALESCE(names->>'primary', names->>'common', 'Place') as name, 'place' as category, ST_Y(ST_Centroid(location)) as latitude, ST_X(ST_Centroid(location)) as longitude, '' as address, 'overture' as region FROM overture_place WHERE names->>'primary' IS NOT NULL LIMIT 500
@@ -520,6 +670,7 @@ export class GeocodingService {
case 'syria': return this.placesSyriaRepository;
case 'jordan': return this.placesJordanRepository;
case 'egypt': return this.placesEgyptRepository;
case 'iraq': return this.placesIraqRepository;
default: throw new HttpException('Invalid country: ' + country, HttpStatus.BAD_REQUEST);
}
}
@@ -5,6 +5,7 @@ import { MapCandidate, CandidateStatus, CountryCode } from './entities/map-candi
import { PlaceJordan } from './entities/place-jordan.entity';
import { PlaceSyria } from './entities/place-syria.entity';
import { PlaceEgypt } from './entities/place-egypt.entity';
import { PlaceIraq } from './entities/place-iraq.entity';
@Injectable()
export class MapRefinementService {
@@ -20,6 +21,8 @@ export class MapRefinementService {
private syriaRepository: Repository<PlaceSyria>,
@InjectRepository(PlaceEgypt)
private egyptRepository: Repository<PlaceEgypt>,
@InjectRepository(PlaceIraq)
private iraqRepository: Repository<PlaceIraq>,
) {}
async suggestPlace(dto: any, submittedBy: string): Promise<MapCandidate> {
@@ -194,6 +197,7 @@ export class MapRefinementService {
switch (candidate.country) {
case CountryCode.SYRIA: repo = this.syriaRepository; break;
case CountryCode.EGYPT: repo = this.egyptRepository; break;
case CountryCode.IRAQ: repo = this.iraqRepository; break;
default: repo = this.jordanRepository;
}