Research and enrich retail location data from web sources. Use when populating missing database fields for shopping centres, retail parks, or outlet centres. Triggers on "enrich location", "research location", "populate location data", "find location info", "complete location fields". WARNING: Do not use browser_subagent unless absolutely necessary.
Purpose: Systematically research locations to populate missing database fields.
Tool Usage Strategy (CRITICAL):
- Tier 1:
search_web- ALWAYS try this first for facts (years, reviews, owners).- Tier 2:
read_url_content- Use when you have a specific URL (website, wiki).- Tier 3:
browser_subagent- AVOID. Only use as absolute last resort if 403/404 errors block other methods. It is too slow for this workflow.- FAILURE MODE: IF
search_webfails (500/API Error), DO NOT STOP. Skip to the next phase (e.g., Demographics) or use best estimates. Label the update as "Partial Enrichment".
audit_location.py to see missing fieldsgenerate_enrichment.py to create update file| Priority | Category | Key Fields |
|---|---|---|
| P0 - Core | Contact | website, phone, openingHours |
| P1 - Operational | Operations | parkingSpaces, retailSpace, numberOfStores, anchorTenants |
| P2 - Commercial | Ownership | owner, management, openedYear, footfall |
| P3 - Digital | Social/Reviews | instagram, facebook, googleRating, googleReviews |
| P4 - Demographic | Census | population, medianAge, avgHouseholdIncome |
🔴 MANDATORY: All tenant categories MUST use the canonical LDC taxonomy. See ldc-taxonomy.md for the full list of valid values.
category = canonical T2 name (e.g., Clothing & Footwear, Cafes & Restaurants)subcategory = canonical T3 name (e.g., Womenswear, Coffee Shop)categoryId at insert time using getCategoryId() from @/lib/category-lookupimport { getCategoryId } from "../src/lib/category-lookup";
// Inside insertTenants():
const categoryId = await getCategoryId(prisma, t.category, t.subcategory);
await prisma.tenant.create({
data: {
locationId: LOCATION_ID,
name: t.name,
category: t.category,
subcategory: t.subcategory || null,
categoryId, // ← resolved from canonical taxonomy
isAnchorTenant: t.isAnchorTenant || false,
},
});
| ❌ Wrong | ✅ Correct |
|---|---|
category: "Restaurant" | category: "Cafes & Restaurants", subcategory: "Restaurant" |
category: "Fashion & Clothing" | category: "Clothing & Footwear" |
category: "Food & Beverage" | category: "Cafes & Restaurants" |
category: "Other" | Find closest T2 match, or category: "General Retail" |
Search: "[location name] official website"
Search: "[location name] shopping centre"
Extract: URL → `website` field
Source: Official website contact/visitor page
Source: Google Maps listing
Extract: phone, openingHours JSON
Source: Official website parking/facilities page
Source: Store directory (count stores)
Extract: parkingSpaces, numberOfStores, carParkPrice, evCharging
Search: "[location name] owner"
Search: "[location name] acquired"
Source: PropertyData, CoStar, company announcements
Extract: owner, management, openedYear
Search: "[location name]" site:instagram.com
Search: "[location name]" site:facebook.com
Verify: Check it's official (verified badge, consistent branding)
Extract: instagram, facebook, twitter, tiktok, youtube
Source: Google Maps - search location name
Source: Facebook page reviews tab
Extract: googleRating (X.X), googleReviews (count), facebookRating, facebookReviews
1. Identify LTLA district for location's city
2. Search ONS Census 2021 data for that LTLA
3. Extract: population, medianAge, familiesPercent, seniorsPercent
4. Calculate vs national averages
{"Mon-Sat": "09:00-18:00", "Sun": "11:00-17:00"}
Or as string: "Mon-Sat 09:00-18:00, Sun 11:00-17:00"
[
{"keyword": "location name", "position": 1, "volume": 2900},
{"keyword": "shops in city", "position": 3, "volume": 1200}
]
[
{"url": "/stores", "traffic": 8500, "percentage": 28},
{"url": "/parking", "traffic": 4800, "percentage": 16}
]
| Script | Usage |
|---|---|
audit_location.py | python scripts/audit_location.py "Location Name" |
generate_enrichment.py | python scripts/generate_enrichment.py <location-id> |
validate_data.py | python scripts/validate_data.py <json-file> |
| Data Type | Primary Source | Backup Source |
|---|---|---|
| Website/Contact | Official site | Google Maps |
| Parking | Official site | Parkopedia |
| Ownership | Company site | PropertyData |
| EV Charging | Official site | Zap-Map, PlugShare |
| Footfall | Press releases | Owner annual report |
| Demographics | ONS Census 2021 | NOMIS |
| Social | Platform search | Google search |
Before submitting enrichment:
category values are canonical LDC T2 namessubcategory values are canonical LDC T3 namescategoryId resolved via getCategoryId() for all tenantsFor detailed guidance: