🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
215 lines
4.9 KiB
Markdown
215 lines
4.9 KiB
Markdown
---
|
|
id: "RF-SCR-003"
|
|
title: "Pipeline ETL y Normalizacion"
|
|
type: "Functional Requirement"
|
|
epic: "IAI-007"
|
|
priority: "Alta"
|
|
status: "Draft"
|
|
project: "inmobiliaria-analytics"
|
|
created_date: "2026-01-04"
|
|
updated_date: "2026-01-04"
|
|
---
|
|
|
|
# RF-IA-007-003: Pipeline ETL y Normalizacion
|
|
|
|
---
|
|
|
|
## Descripcion
|
|
|
|
El sistema debe procesar los datos raw extraidos de multiples fuentes, normalizarlos a un schema unificado, enriquecerlos con geocoding y cargarlos en la base de datos de propiedades.
|
|
|
|
---
|
|
|
|
## Justificacion
|
|
|
|
Cada portal inmobiliario tiene su propia estructura de datos y nomenclatura. Para poder realizar analytics consistentes, los datos deben normalizarse a un schema comun con campos estandarizados.
|
|
|
|
---
|
|
|
|
## Requisitos Funcionales
|
|
|
|
### RF-003.1: Extraccion
|
|
|
|
| ID | Requisito | Prioridad |
|
|
|----|-----------|-----------|
|
|
| RF-003.1.1 | El sistema debe parsear HTML a JSON estructurado | Alta |
|
|
| RF-003.1.2 | El sistema debe extraer todos los campos definidos en el schema | Alta |
|
|
| RF-003.1.3 | El sistema debe manejar variaciones en estructura HTML | Alta |
|
|
| RF-003.1.4 | El sistema debe almacenar raw data en storage persistente | Media |
|
|
|
|
### RF-003.2: Transformacion
|
|
|
|
| ID | Requisito | Prioridad |
|
|
|----|-----------|-----------|
|
|
| RF-003.2.1 | El sistema debe normalizar tipos de propiedad a enum unificado | Alta |
|
|
| RF-003.2.2 | El sistema debe convertir precios a formato numerico estandar | Alta |
|
|
| RF-003.2.3 | El sistema debe normalizar unidades de superficie (m2) | Alta |
|
|
| RF-003.2.4 | El sistema debe extraer caracteristicas de texto libre | Media |
|
|
| RF-003.2.5 | El sistema debe limpiar y estandarizar direcciones | Alta |
|
|
|
|
### RF-003.3: Enriquecimiento
|
|
|
|
| ID | Requisito | Prioridad |
|
|
|----|-----------|-----------|
|
|
| RF-003.3.1 | El sistema debe geocodificar direcciones a lat/lon | Alta |
|
|
| RF-003.3.2 | El sistema debe calcular precio por m2 | Alta |
|
|
| RF-003.3.3 | El sistema debe asignar zona/colonia normalizada | Alta |
|
|
| RF-003.3.4 | El sistema debe detectar duplicados cross-source | Media |
|
|
|
|
### RF-003.4: Carga
|
|
|
|
| ID | Requisito | Prioridad |
|
|
|----|-----------|-----------|
|
|
| RF-003.4.1 | El sistema debe hacer upsert en tabla de propiedades | Alta |
|
|
| RF-003.4.2 | El sistema debe mantener historial de cambios de precio | Alta |
|
|
| RF-003.4.3 | El sistema debe marcar propiedades inactivas | Alta |
|
|
| RF-003.4.4 | El sistema debe emitir eventos de nuevas propiedades | Media |
|
|
|
|
---
|
|
|
|
## Schema Normalizado
|
|
|
|
```yaml
|
|
Property:
|
|
# Identificacion
|
|
id: UUID
|
|
source: string # inmuebles24, vivanuncios, etc
|
|
source_id: string # ID original del portal
|
|
source_url: string
|
|
|
|
# Tipo
|
|
property_type: enum
|
|
- house
|
|
- apartment
|
|
- land
|
|
- commercial
|
|
- office
|
|
- warehouse
|
|
transaction_type: enum [sale, rent]
|
|
|
|
# Ubicacion
|
|
address: string
|
|
neighborhood: string
|
|
city: string
|
|
state: string
|
|
postal_code: string
|
|
latitude: decimal
|
|
longitude: decimal
|
|
zone_id: UUID (FK)
|
|
|
|
# Caracteristicas
|
|
bedrooms: integer
|
|
bathrooms: decimal
|
|
parking_spaces: integer
|
|
construction_m2: decimal
|
|
land_m2: decimal
|
|
age_years: integer
|
|
floors: integer
|
|
|
|
# Precio
|
|
price: decimal
|
|
currency: string # MXN, USD
|
|
price_per_m2: decimal (calculado)
|
|
|
|
# Descripcion
|
|
title: string
|
|
description: text
|
|
amenities: string[]
|
|
images: string[]
|
|
|
|
# Metadata
|
|
is_active: boolean
|
|
first_seen_at: timestamp
|
|
last_seen_at: timestamp
|
|
price_history: JSONB
|
|
created_at: timestamp
|
|
updated_at: timestamp
|
|
```
|
|
|
|
---
|
|
|
|
## Mappings por Fuente
|
|
|
|
### Inmuebles24
|
|
|
|
```yaml
|
|
mappings:
|
|
property_type:
|
|
"Casa": house
|
|
"Departamento": apartment
|
|
"Terreno": land
|
|
"Local comercial": commercial
|
|
"Oficina": office
|
|
"Bodega": warehouse
|
|
|
|
transaction_type:
|
|
"Venta": sale
|
|
"Renta": rent
|
|
"Venta o Renta": sale # priorizar venta
|
|
|
|
precio:
|
|
selector: ".price-value"
|
|
transform: "remove_currency_symbols | to_number"
|
|
|
|
superficie:
|
|
selector: ".surface-value"
|
|
transform: "extract_number | assume_m2"
|
|
```
|
|
|
|
---
|
|
|
|
## Reglas de Validacion
|
|
|
|
```yaml
|
|
validations:
|
|
precio:
|
|
min: 10000 # MXN
|
|
max: 500000000
|
|
required: true
|
|
|
|
superficie:
|
|
min: 10 # m2
|
|
max: 100000
|
|
required: false
|
|
|
|
coordenadas:
|
|
latitude_range: [14.5, 32.7] # Mexico
|
|
longitude_range: [-118.5, -86.7]
|
|
|
|
deduplicacion:
|
|
strategy: "source_id + source"
|
|
fallback: "address_normalized + price + type"
|
|
```
|
|
|
|
---
|
|
|
|
## Criterios de Aceptacion
|
|
|
|
- [ ] Pipeline procesa 1000 propiedades/hora minimo
|
|
- [ ] Todos los campos requeridos se mapean correctamente
|
|
- [ ] Precios se convierten a formato numerico sin errores
|
|
- [ ] Geocoding tiene 95%+ success rate
|
|
- [ ] Duplicados se detectan con 98%+ precision
|
|
- [ ] Historial de precios se mantiene correctamente
|
|
- [ ] Raw data se almacena para debugging
|
|
|
|
---
|
|
|
|
## Dependencias
|
|
|
|
- Cheerio o similar para parsing HTML
|
|
- Google Maps API para geocoding
|
|
- PostgreSQL para persistencia
|
|
- S3/MinIO para raw data storage
|
|
|
|
---
|
|
|
|
## Historias de Usuario Relacionadas
|
|
|
|
- US-SCR-003: Normalizacion de datos
|
|
|
|
---
|
|
|
|
**Autor:** Tech Lead
|
|
**Fecha:** 2026-01-04
|