Browse Source

many updates. many.

master
Yutsuo 1 month ago
parent
commit
c5e95a6095
  1. 26
      .gitignore
  2. 257
      AGENTS.md
  3. 24
      README.md
  4. BIN
      __pycache__/dfimoveis_scraper.cpython-314.pyc
  5. BIN
      dfimoveis_data/dfimoveis.sqlite3-shm
  6. 0
      dfimoveis_data/dfimoveis.sqlite3-wal
  7. 238
      dfimoveis_scraper.py
  8. 97
      docs/ACTUAL_SCHEMA.md
  9. 610
      docs/ANALYSIS_RULES.md
  10. 90
      docs/CONTEXT_CHANGELOG.md
  11. 53
      docs/CURRENT_SHORTLIST.md
  12. 145
      docs/CURRENT_STATE.md
  13. 537
      docs/DATA_MODEL.md
  14. 195
      docs/FINANCIAL_STRATEGY.md
  15. 138
      docs/LOCATION_CRITERIA.md
  16. 308
      docs/PROJECT_CONTEXT.md
  17. 536
      docs/PROPERTY_CRITERIA.md
  18. 206
      docs/SCRAPER_OPERATIONS.md
  19. 55
      reports/creci_26903_mangueiral/README.md
  20. 15
      reports/creci_26903_mangueiral/query.sql
  21. 16
      reports/creci_26903_mangueiral/results.csv
  22. 172
      reports/cruzeiro_top10/README.md
  23. BIN
      reports/cruzeiro_top10/__pycache__/analysis.cpython-314.pyc
  24. BIN
      reports/cruzeiro_top10/__pycache__/photo_similarity.cpython-314.pyc
  25. 22
      reports/cruzeiro_top10/parameters.json
  26. 11
      reports/cruzeiro_top10/results.csv
  27. 64
      reports/cruzeiro_top15/README.md
  28. 2
      reports/cruzeiro_top15/analysis.py
  29. 20
      reports/cruzeiro_top15/parameters.json
  30. 6
      reports/cruzeiro_top15/photo_similarity.py
  31. 1
      reports/cruzeiro_top15/query.sql
  32. 16
      reports/cruzeiro_top15/results.csv
  33. 179
      reports/cruzeiro_vs_mangueiral/README.md
  34. 46
      reports/cruzeiro_vs_mangueiral/parameters.json
  35. 52
      reports/cruzeiro_vs_mangueiral/results.csv
  36. 358
      reports/database_assessment.md
  37. 184
      reports/mangueiral_top10/README.md
  38. BIN
      reports/mangueiral_top10/__pycache__/analysis.cpython-312.pyc
  39. BIN
      reports/mangueiral_top10/__pycache__/analysis.cpython-314.pyc
  40. BIN
      reports/mangueiral_top10/__pycache__/photo_similarity.cpython-312.pyc
  41. BIN
      reports/mangueiral_top10/__pycache__/photo_similarity.cpython-314.pyc
  42. 29
      reports/mangueiral_top10/parameters.json
  43. 11
      reports/mangueiral_top10/results.csv
  44. 70
      reports/mangueiral_top15/README.md
  45. 2
      reports/mangueiral_top15/analysis.py
  46. 38
      reports/mangueiral_top15/parameters.json
  47. 6
      reports/mangueiral_top15/photo_similarity.py
  48. 3
      reports/mangueiral_top15/query.sql
  49. 16
      reports/mangueiral_top15/results.csv
  50. 2
      requirements-dfimoveis.txt
  51. 5
      run_scraper.ps1
  52. 169
      scripts/migrate_dfimoveis_schema_v2.py
  53. 1
      search_list.txt
  54. 64
      tests/test_dfimoveis_schema_v2.py

26
.gitignore vendored

@ -1 +1,25 @@
dfimoveis_data # Banco e arquivos transitórios
*.sqlite
*.sqlite3
*.db
*.sqlite-wal
*.sqlite-shm
# Dados coletados
/dfimoveis_data/photos/
/dfimoveis_data/raw/
/dfimoveis_data/browser-profile/
# Derivações grandes
/data/derived/
/exports/
# Python
__pycache__/
*.py[cod]
.venv/
# Segredos e sessões
.env
.env.*
*.cookies.json

257
AGENTS.md

@ -1,191 +1,200 @@
# AGENTS.md — Busca de Imóveis DF # AGENTS.md — Busca de Imóveis DF
## Finalidade ## Missão
Este repositório apoia a coleta, organização e análise histórica de anúncios imobiliários do Distrito Federal para uma decisão residencial de longo prazo. Apoiar uma decisão de compra residencial de longo prazo no Distrito Federal usando o banco histórico de anúncios, as fotografias e os critérios pessoais consolidados do usuário.
Antes de trabalhar no projeto, leia integralmente: O trabalho deve ser cético, reproduzível e orientado a evitar uma compra ruim. Não maximizar apenas preço por metro quadrado, valorização ou número de atributos anunciados.
## Leitura obrigatória
Antes de analisar imóveis ou alterar scripts, leia:
- `docs/CURRENT_STATE.md`
- `docs/PROJECT_CONTEXT.md` - `docs/PROJECT_CONTEXT.md`
- `docs/LOCATION_CRITERIA.md`
- `docs/PROPERTY_CRITERIA.md` - `docs/PROPERTY_CRITERIA.md`
- `docs/FINANCIAL_STRATEGY.md`
- `docs/SCRAPER_OPERATIONS.md`
- `docs/DATA_MODEL.md` - `docs/DATA_MODEL.md`
- `docs/ANALYSIS_RULES.md` - `docs/ANALYSIS_RULES.md`
Esses documentos são a referência operacional do projeto. Quando houver conflito: Quando houver conflito, use esta precedência:
1. uma instrução explícita e recente do usuário prevalece; 1. instrução explícita e recente do usuário;
2. depois, prevalecem estes documentos; 2. `docs/CURRENT_STATE.md`;
3. por último, hipóteses inferidas a partir dos dados. 3. documento especializado do tema;
4. snapshot ou relatório datado;
5. contexto histórico e inferências.
Nunca trate uma hipótese como preferência confirmada. Nunca transforme inferência, relato de corretor, percepção de morador ou padrão do banco em preferência confirmada.
Para auxiliar no entendimento e contexto, as plantas das casas dos Jardins Mangueiral estão em: ---
- `resources/plans` ## Prioridade regional vigente
Para auxiliar no entendimento e contexto, foram feitas capturas dos mapas dos Jardins Mangueiral, que foram salvas na em: - **Prioridade principal:** Jardins Mangueiral.
- **Alternativa secundária e oportunística:** Cruzeiro, especialmente Cruzeiro Novo.
- `resources/maps` O ranking interno de quadras do Cruzeiro continua sendo usado para avaliar apartamentos concretos, mas não torna o Cruzeiro prioritário sobre o Mangueiral.
--- ---
## Banco de dados ## Regras pessoais obrigatórias
O banco principal deve ser tratado como **estritamente somente leitura**. ### Jardins Mangueiral
### Regras obrigatórias - Considerar somente casas com **planta original de três quartos**.
- Excluir plantas originalmente de dois quartos, mesmo que tenham sido adaptadas.
- Não penalizar automaticamente o fechamento do quintal.
- Avaliar se a conversão cria espaço social útil, iluminado, ventilado e sem redundância funcional.
- Usar a hierarquia de QCs de `LOCATION_CRITERIA.md`, sem ignorar a posição exata da casa.
- Avaliar explicitamente ruído de vizinhos, animais, música, áreas sociais, ruas e equipamentos comuns.
- Dar preferência a casas reformadas, com bom espaço na cozinha.
- Nunca execute `INSERT`, `UPDATE`, `DELETE`, `DROP`, `ALTER`, `CREATE`, `REPLACE`, `VACUUM`, `REINDEX`, `ATTACH` ou comandos equivalentes contra o banco original. ### Cruzeiro
- Não substitua, mova, compacte, migre ou renomeie o arquivo original.
- Não remova arquivos `-wal` ou `-shm` sem uma instrução explícita.
- Não execute scripts de scraping contra o banco durante uma análise sem autorização.
- Use `LIMIT` em consultas exploratórias.
- Não imprima tabelas inteiras no terminal.
- Prefira agregações, amostras pequenas e exportações derivadas.
- Salve resultados em `reports/`, `exports/` ou `data/derived/`.
- Nunca grave resultados derivados dentro do banco original.
- Antes de qualquer análise, confirme o caminho do banco e abra-o em modo somente leitura.
### Abertura recomendada em Python - Tratar como oportunidade secundária.
- Usar o ranking interno Ouro/Prata/Bronze de `LOCATION_CRITERIA.md`.
- Uma unidade fora da faixa superior pode vencer pela combinação de reforma, silêncio, planta, andar, elevador, garagem e preço.
- Ausência de elevador e garagem é concessão relevante.
- Confirmar se os três quartos são utilizáveis e coerentes com a planta.
- Inspecionar sistemas antigos, condomínio, fachada, prumadas, elétrica, hidráulica e obras previstas.
```python ---
import sqlite3
DB_PATH = "data/imoveis.sqlite" ## Banco principal
connection = sqlite3.connect( Caminho esperado:
f"file:{DB_PATH}?mode=ro&immutable=1",
uri=True, ```text
) dfimoveis_data/dfimoveis.sqlite3
connection.execute("PRAGMA query_only = ON")
``` ```
Se o banco estiver sendo atualizado por outro processo, não use `immutable=1`; mantenha `mode=ro` e confirme a consistência do snapshot antes de analisar. O banco original é **estritamente somente leitura durante análises**.
### Cliente de linha de comando ### Proibições
```bash Nunca executar contra o banco original:
sqlite3 -readonly data/imoveis.sqlite
```
--- - `INSERT`
- `UPDATE`
- `DELETE`
- `DROP`
- `ALTER`
- `CREATE`
- `REPLACE`
- `VACUUM`
- `REINDEX`
- `ATTACH`
## Fluxo inicial obrigatório Não mover, substituir, compactar, migrar ou renomear o banco sem instrução explícita. Não remover arquivos `-wal` ou `-shm` arbitrariamente.
Ao iniciar uma nova tarefa de análise: ### Abertura recomendada
1. Identifique o arquivo SQLite correto e seu tamanho. ```python
2. Leia `sqlite_master` para listar tabelas, views, índices e triggers. import sqlite3
3. Examine o esquema real antes de escrever consultas analíticas.
4. Conte registros por tabela.
5. Identifique campos de data, preço, área, quartos, localização, URL, anunciante e identificadores.
6. Verifique valores nulos, duplicidades, formatos inconsistentes e possíveis quebras de coleta.
7. Descubra se os dados representam:
- anúncios;
- imóveis físicos;
- observações históricas do mesmo anúncio;
- execuções do scraper.
8. Documente qualquer ambiguidade antes de produzir conclusões.
Não presuma que o modelo lógico descrito em `docs/DATA_MODEL.md` já existe fisicamente. DB_PATH = "dfimoveis_data/dfimoveis.sqlite3"
connection = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True)
connection.execute("PRAGMA query_only = ON")
```
Use `immutable=1` somente em cópia ou snapshot que não esteja sendo atualizado pelo scraper.
--- ---
## Princípios de análise ## Separação entre scraper e análise
- Diferencie sempre **anúncio**, **imóvel físico** e **observação histórica**. - Não executar coleta e análise pesada simultaneamente no mesmo arquivo SQLite sem avaliar bloqueios e consistência.
- Um mesmo imóvel pode aparecer em vários anúncios, portais, corretores ou datas. - Para análise longa, preferir uma cópia/snapshot consistente ou conexão read-only.
- Um mesmo anúncio pode mudar de preço, descrição, área informada ou status. - Resultados derivados devem ir para `reports/`, `exports/` ou `data/derived/`.
- Preço anunciado não é preço efetivamente negociado. - Nunca gravar rankings, notas ou tabelas derivadas dentro do banco original sem autorização.
- Ausência de anúncio não prova venda.
- Remoção de anúncio não prova fechamento de negócio.
- Campo preenchido pelo anunciante não deve ser considerado confiável sem validação.
- Não calcule preço por metro quadrado quando a área não for comparável ou confiável.
- Não misture área privativa, útil, construída e total sem separar os conceitos.
- Não compare períodos sem verificar mudanças no universo coletado.
- Não compare regiões sem controlar tipologia, quartos, área, garagem, estado e padrão do imóvel.
- Sempre informe tamanho da amostra, cobertura temporal, filtros e limitações.
- Prefira mediana, percentis e intervalos interquartis a médias isoladas.
- Mostre a sensibilidade das conclusões a outliers, duplicidades e dados ausentes.
--- ---
## Segurança e desempenho ## Fluxo analítico obrigatório
- Nunca exponha credenciais, cookies, tokens, chaves, sessões ou dados pessoais em relatórios. 0. Verificar se a primeira foto do anúncio não demonstra que ele foi vendido.
- Não envie o banco ou grandes amostras para serviços externos sem autorização explícita. 1. Confirmar o snapshot e a cobertura temporal.
- Não tente contornar mecanismos de proteção de sites. 2. Ler o esquema real.
- Respeite limites de requisição, termos aplicáveis e regras definidas para o scraper. 3. Identificar anúncios, observações históricas, fotos e execuções de coleta.
- Evite consultas que façam varreduras desnecessárias repetidas em tabelas grandes. 4. Verificar falhas de coleta e mudanças do scraper.
- Use `EXPLAIN QUERY PLAN` em consultas lentas. 5. Separar anúncio, observação e imóvel físico provável.
- Sugira índices apenas como recomendação; não os crie no banco original. 6. Deduplicar republicações sem apagar registros originais.
- Para transformações pesadas, crie um banco derivado ou arquivos Parquet/CSV separados. 7. Auditar a primeira foto e a descrição para “VENDIDO”, “negócio fechado” ou equivalente.
8. Colocar inconsistências em quarentena, sem exclusão silenciosa.
9. Avaliar fotografias; CSV isolado não é evidência suficiente para planta e conservação.
10. Registrar amostra, filtros, exclusões, limitações e método.
--- ---
## Código ## Regras analíticas essenciais
- Escreva scripts reproduzíveis, parametrizados e idempotentes. - Preço anunciado não é preço negociado.
- Use caminhos relativos ao repositório. - Desaparecimento do anúncio não prova venda.
- Evite constantes escondidas. - Selo de vendido é evidência operacional, não prova registral.
- Registre os filtros e premissas usados. - Não misturar área privativa, útil, construída, total e terreno.
- Inclua tratamento de erros. - Não usar preço por m² quando a área não for comparável ou confiável.
- Use consultas SQL parametrizadas. - Não multiplicar republicações visualmente equivalentes.
- Adicione testes para regras de normalização, deduplicação e cálculo. - Não considerar “reformado” como prova de renovação elétrica, hidráulica ou estrutural.
- Não altere arquivos sem relação com a tarefa. - Não atribuir causalidade à QC ou quadra sem controlar tipologia e qualidade.
- Não faça refatorações amplas sem necessidade. - Não inferir liquidez com uma janela curta de coleta.
- Não invente significado para colunas pouco claras; documente a dúvida. - Rankings servem para ordenar investigação e visitas, não para automatizar a compra.
--- ---
## Saídas esperadas ## Finanças
Relatórios analíticos devem, sempre que aplicável, conter: Antes de recomendar compra ou preço máximo, leia `FINANCIAL_STRATEGY.md`.
1. pergunta respondida; Regras fixas:
2. período e universo analisados;
3. filtros e exclusões;
4. tamanho da amostra;
5. método de deduplicação;
6. qualidade dos dados;
7. resultados;
8. limitações;
9. implicações para a decisão de compra;
10. consultas ou scripts usados para reproduzir o resultado.
Use gráficos apenas quando eles melhorarem a interpretação. Todo gráfico deve ter título, unidade, período, tamanho da amostra e definição da métrica. - preservar pelo menos R$ 50.000 de reserva líquida após compra, custos e adequações;
- não tratar FGTS como reserva de emergência;
- excluir Pró-Cotista e MCMV Faixa 4 das simulações atuais;
- distinguir entrada, custos de compra, reforma, reserva e amortizações futuras;
- comparar financiamento, consórcio e aluguel com premissas datadas;
- não usar taxas antigas como atuais;
- incorporar fatores psicológicos sem convertê-los em justificativa para um imóvel inadequado.
--- ---
## Contexto imobiliário prioritário ## Código e relatórios
As regiões prioritárias são: - Escrever scripts reproduzíveis, parametrizados e idempotentes.
- Usar SQL parametrizado.
- Não imprimir milhares de linhas no terminal.
- Usar `LIMIT` na exploração.
- Usar `EXPLAIN QUERY PLAN` quando necessário.
- Sugerir índices sem criá-los no banco original.
- Preservar HTML bruto, fotos e dados originais.
- Toda conclusão deve separar fato, inferência, percepção e pendência.
1. Cruzeiro, especialmente Cruzeiro Novo; Um relatório deve conter:
2. Jardins Mangueiral.
Não exclua outras regiões de uma análise exploratória quando forem úteis como referência, mas não desvie o projeto de seu objetivo principal sem instrução. 1. pergunta;
2. snapshot e período;
Para Jardins Mangueiral: 3. universo e amostra;
4. filtros e quarentenas;
- considerar somente casas com planta original de três quartos; 5. método de deduplicação;
- não penalizar automaticamente o fechamento do quintal; 6. avaliação fotográfica;
- avaliar a funcionalidade real da conversão; 7. resultados;
- considerar as preferências de QC descritas em `docs/PROPERTY_CRITERIA.md`; 8. limitações;
- tratar relatos de moradores como evidência anedótica, não como fato conclusivo. 9. implicação prática;
10. consultas e scripts para reprodução.
--- ---
## Atualização do contexto ## Atualização do contexto
Não altere estes documentos apenas porque encontrou um padrão no banco. Atualizar os documentos somente quando:
Atualize-os somente quando:
- o usuário corrigir uma premissa; - o usuário corrigir uma premissa;
- uma preferência for explicitamente confirmada; - uma preferência for explicitamente confirmada;
- um valor financeiro for atualizado; - houver nova visita relevante;
- uma regra metodológica for aprovada; - um snapshot substituir o anterior;
- o esquema real do projeto for consolidado. - uma regra financeira ou operacional mudar;
- o esquema real for documentado.
Ao atualizar uma premissa financeira, registre a data de referência e preserve o histórico em Git. Registrar a mudança em `docs/CONTEXT_CHANGELOG.md` com data e motivo.

24
README.md

@ -2,10 +2,7 @@
Projeto pessoal para coletar, organizar e analisar anúncios imobiliários do Distrito Federal, com foco em uma decisão residencial de longo prazo. Projeto pessoal para coletar, organizar e analisar anúncios imobiliários do Distrito Federal, com foco em uma decisão residencial de longo prazo.
As regiões prioritárias são: A prioridade operacional atual é o Jardins Mangueiral. O Cruzeiro permanece apenas como histórico e alternativa oportunística; sua URL foi removida da coleta corrente.
1. Cruzeiro, especialmente Cruzeiro Novo;
2. Jardins Mangueiral.
O objetivo não é identificar simplesmente o menor preço anunciado. A análise considera funcionalidade, estado do imóvel, risco de reforma, localização, liquidez, custos de aquisição e manutenção e adequação à vida familiar. O objetivo não é identificar simplesmente o menor preço anunciado. A análise considera funcionalidade, estado do imóvel, risco de reforma, localização, liquidez, custos de aquisição e manutenção e adequação à vida familiar.
@ -21,8 +18,8 @@ O repositório contém:
Relatórios principais: Relatórios principais:
- [Top 10 — Cruzeiro Novo](reports/cruzeiro_top10/README.md) - [Top 15 — Cruzeiro Novo](reports/cruzeiro_top15/README.md)
- [Top 10 — Jardins Mangueiral](reports/mangueiral_top10/README.md) - [Top 15 — Jardins Mangueiral](reports/mangueiral_top15/README.md)
- [Cruzeiro Novo x Jardins Mangueiral](reports/cruzeiro_vs_mangueiral/README.md) - [Cruzeiro Novo x Jardins Mangueiral](reports/cruzeiro_vs_mangueiral/README.md)
Os rankings são prioridades de investigação, não recomendações automáticas de compra nem estimativas de preço negociado. Os rankings são prioridades de investigação, não recomendações automáticas de compra nem estimativas de preço negociado.
@ -82,10 +79,7 @@ As ferramentas de similaridade fotográfica usam Pillow, incluído no arquivo de
## Configuração das buscas ## Configuração das buscas
O arquivo [search_list.txt](search_list.txt) contém uma URL completa por linha. Atualmente ele cobre: O arquivo [search_list.txt](search_list.txt) contém uma URL completa por linha. Atualmente ele cobre casas anunciadas com três ou quatro quartos no Jardins Mangueiral.
- apartamentos de três quartos no Cruzeiro Novo;
- casas anunciadas com três ou quatro quartos no Jardins Mangueiral.
A busca do Mangueiral inclui quatro quartos para ampliar a coleta, mas esses imóveis são excluídos da análise regional. Mesmo três quartos anunciados não comprovam a planta original. A busca do Mangueiral inclui quatro quartos para ampliar a coleta, mas esses imóveis são excluídos da análise regional. Mesmo três quartos anunciados não comprovam a planta original.
@ -138,6 +132,8 @@ dfimoveis_data/dfimoveis.sqlite3
Ele deve ser tratado como estritamente somente leitura durante análises. Nunca grave resultados derivados dentro dele. Ele deve ser tratado como estritamente somente leitura durante análises. Nunca grave resultados derivados dentro dele.
O schema v2 preserva os valores brutos e oferece campos normalizados, quarentenas e visões auditáveis. Consultas de fotos devem preferir `v_primary_photos`; a migração idempotente está em `scripts/migrate_dfimoveis_schema_v2.py`.
Quando não houver processo de coleta e não existir WAL relevante: Quando não houver processo de coleta e não existir WAL relevante:
```python ```python
@ -180,15 +176,15 @@ reports/<relatorio>/
Extração tabular somente leitura: Extração tabular somente leitura:
```powershell ```powershell
.\.venv-scraper\Scripts\python.exe .\reports\cruzeiro_top10\analysis.py .\.venv-scraper\Scripts\python.exe .\reports\cruzeiro_top15\analysis.py
.\.venv-scraper\Scripts\python.exe .\reports\mangueiral_top10\analysis.py .\.venv-scraper\Scripts\python.exe .\reports\mangueiral_top15\analysis.py
``` ```
Triagem de possíveis duplicatas por similaridade visual: Triagem de possíveis duplicatas por similaridade visual:
```powershell ```powershell
.\.venv-scraper\Scripts\python.exe .\reports\cruzeiro_top10\photo_similarity.py .\.venv-scraper\Scripts\python.exe .\reports\cruzeiro_top15\photo_similarity.py
.\.venv-scraper\Scripts\python.exe .\reports\mangueiral_top10\photo_similarity.py .\.venv-scraper\Scripts\python.exe .\reports\mangueiral_top15\photo_similarity.py
``` ```
Similaridade de imagem é apenas um sinal para revisão humana, nunca prova automática de que dois anúncios representam o mesmo imóvel. Similaridade de imagem é apenas um sinal para revisão humana, nunca prova automática de que dois anúncios representam o mesmo imóvel.

BIN
__pycache__/dfimoveis_scraper.cpython-314.pyc

Binary file not shown.

BIN
dfimoveis_data/dfimoveis.sqlite3-shm

Binary file not shown.

0
dfimoveis_data/dfimoveis.sqlite3-wal

238
dfimoveis_scraper.py

@ -138,6 +138,44 @@ def safe_name(value: str) -> str:
return re.sub(r"[^a-zA-Z0-9._-]+", "_", value).strip("_")[:120] or "unnamed" return re.sub(r"[^a-zA-Z0-9._-]+", "_", value).strip("_")[:120] or "unnamed"
def normalize_creci(value: str | None) -> str | None:
"""Return only the registration number, preserving the raw portal value elsewhere."""
if not value:
return None
match = re.search(r"(?<!\d)(\d{1,3}(?:\.\d{3})+|\d{4,6})(?!\d)", value)
return re.sub(r"\D", "", match.group(1)) if match else None
def normalize_listing_address(url: str, address: str | None) -> str | None:
"""Build a conservative location label without retaining portal UI text."""
slug = urlparse(url).path.lower().replace("-", " ")
qc = re.search(r"\bqc\s*(\d{1,2})\b", slug)
street = re.search(r"\brua\s+([a-z0-9]+)\b", slug)
parts = ["Jardins Mangueiral"] if "jardins mangueiral" in slug else []
if qc:
parts.append(f"QC {int(qc.group(1))}")
if street:
parts.append(f"Rua {street.group(1).upper()}")
if parts:
return " - ".join(parts)
# JSON-LD addresses are normally short. Reject known UI-contaminated fallbacks.
cleaned = clean_text(address)
return cleaned if cleaned and len(cleaned) <= 250 else None
def normalize_description_for_hash(value: str | None) -> str | None:
cleaned = clean_text(value)
if not cleaned:
return None
cleaned = re.sub(
r"(?:última|ultima)\s+atualiza(?:ção|cao)\s*:\s*\d{2}/\d{2}/\d{2,4}\s+\d{2}:\d{2}",
"última atualização: <data>",
cleaned,
flags=re.I,
)
return cleaned
@dataclasses.dataclass(slots=True) @dataclasses.dataclass(slots=True)
class Config: class Config:
searches: list[str] searches: list[str]
@ -245,35 +283,109 @@ class Database:
status TEXT NOT NULL, status TEXT NOT NULL,
stats_json TEXT NOT NULL DEFAULT '{}' stats_json TEXT NOT NULL DEFAULT '{}'
); );
CREATE TABLE IF NOT EXISTS availability_observations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
listing_id TEXT NOT NULL,
observed_at TEXT NOT NULL,
portal_active INTEGER,
classification TEXT NOT NULL,
source TEXT NOT NULL,
evidence TEXT,
run_id INTEGER,
FOREIGN KEY (listing_id) REFERENCES listings(listing_id),
FOREIGN KEY (run_id) REFERENCES crawl_runs(id)
);
CREATE TABLE IF NOT EXISTS data_quality_issues (
id INTEGER PRIMARY KEY AUTOINCREMENT,
listing_id TEXT,
issue_code TEXT NOT NULL,
severity TEXT NOT NULL,
evidence TEXT,
first_seen_at TEXT NOT NULL,
last_seen_at TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'open',
UNIQUE(listing_id, issue_code),
FOREIGN KEY (listing_id) REFERENCES listings(listing_id)
);
CREATE TABLE IF NOT EXISTS listing_change_events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
listing_id TEXT NOT NULL,
captured_at TEXT NOT NULL,
event_type TEXT NOT NULL,
changes_json TEXT NOT NULL,
stable_content_hash TEXT NOT NULL,
run_id INTEGER,
FOREIGN KEY (listing_id) REFERENCES listings(listing_id),
FOREIGN KEY (run_id) REFERENCES crawl_runs(id)
);
"""
)
self._ensure_column("listings", "address_normalized", "TEXT")
self._ensure_column("listings", "neighborhood_normalized", "TEXT")
self._ensure_column("listings", "creci_normalized", "TEXT")
self._ensure_column("listings", "raw_content_hash", "TEXT")
self._ensure_column("listings", "stable_content_hash", "TEXT")
self._ensure_column("photos", "discovery_source", "TEXT")
self._ensure_column("photos", "is_primary_gallery", "INTEGER NOT NULL DEFAULT 1")
self._ensure_column("photos", "review_status", "TEXT NOT NULL DEFAULT 'unreviewed'")
self._ensure_column("crawl_runs", "searches_json", "TEXT NOT NULL DEFAULT '[]'")
self._ensure_column("crawl_runs", "config_json", "TEXT NOT NULL DEFAULT '{}'")
self._ensure_column("crawl_runs", "scope_complete", "INTEGER")
self.conn.execute("CREATE INDEX IF NOT EXISTS idx_listings_creci_normalized ON listings(creci_normalized)")
self.conn.execute("CREATE INDEX IF NOT EXISTS idx_listings_inactive_at ON listings(inactive_at)")
self.conn.execute("CREATE INDEX IF NOT EXISTS idx_photos_primary ON photos(listing_id,is_primary_gallery,ordinal)")
self.conn.execute("CREATE INDEX IF NOT EXISTS idx_change_events_listing ON listing_change_events(listing_id,captured_at)")
self.conn.executescript(
"""
CREATE VIEW IF NOT EXISTS v_primary_photos AS
SELECT * FROM photos WHERE is_primary_gallery=1;
CREATE VIEW IF NOT EXISTS v_open_data_quality_issues AS
SELECT * FROM data_quality_issues WHERE status='open';
CREATE VIEW IF NOT EXISTS v_listing_current_availability AS
SELECT l.listing_id,l.url,(l.inactive_at IS NULL) AS portal_active,l.inactive_at,
a.classification,a.source,a.evidence,a.observed_at
FROM listings AS l
LEFT JOIN availability_observations AS a ON a.id=(
SELECT a2.id FROM availability_observations AS a2
WHERE a2.listing_id=l.listing_id ORDER BY a2.observed_at DESC,a2.id DESC LIMIT 1
);
""" """
) )
self.conn.commit() self.conn.commit()
def start_run(self) -> int: def _ensure_column(self, table: str, column: str, declaration: str) -> None:
existing = {row[1] for row in self.conn.execute(f"PRAGMA table_info({table})")}
if column not in existing:
self.conn.execute(f"ALTER TABLE {table} ADD COLUMN {column} {declaration}")
def start_run(self, searches: list[str], config: dict[str, Any]) -> int:
cur = self.conn.execute( cur = self.conn.execute(
"INSERT INTO crawl_runs(started_at,status) VALUES(?,?)", (now_utc(), "running") "INSERT INTO crawl_runs(started_at,status,searches_json,config_json,scope_complete) VALUES(?,?,?,?,?)",
(now_utc(), "running", json.dumps(searches, ensure_ascii=False), json.dumps(config, ensure_ascii=False), 0),
) )
self.conn.commit() self.conn.commit()
return int(cur.lastrowid) return int(cur.lastrowid)
def finish_run(self, run_id: int, status: str, stats: dict[str, Any]) -> None: def finish_run(self, run_id: int, status: str, stats: dict[str, Any]) -> None:
self.conn.execute( self.conn.execute(
"UPDATE crawl_runs SET finished_at=?, status=?, stats_json=? WHERE id=?", "UPDATE crawl_runs SET finished_at=?, status=?, stats_json=?, scope_complete=? WHERE id=?",
(now_utc(), status, json.dumps(stats, ensure_ascii=False), run_id), (now_utc(), status, json.dumps(stats, ensure_ascii=False), int(status == "ok" and stats.get("details_failed", 0) == 0), run_id),
) )
self.conn.commit() self.conn.commit()
def upsert_listing(self, data: dict[str, Any], search_url: str) -> bool: def upsert_listing(self, data: dict[str, Any], search_url: str, run_id: int | None = None) -> bool:
ts = now_utc() ts = now_utc()
existing = self.conn.execute( existing = self.conn.execute(
"SELECT content_hash FROM listings WHERE listing_id=?", (data["listing_id"],) "SELECT * FROM listings WHERE listing_id=?", (data["listing_id"],)
).fetchone() ).fetchone()
changed = not existing or existing["content_hash"] != data["content_hash"] changed = not existing or existing["stable_content_hash"] != data["stable_content_hash"]
cols = [ cols = [
"listing_id", "url", "title", "description", "transaction_type", "property_type", "listing_id", "url", "title", "description", "transaction_type", "property_type",
"price_brl", "condominium_brl", "iptu_brl", "area_m2", "bedrooms", "suites", "price_brl", "condominium_brl", "iptu_brl", "area_m2", "bedrooms", "suites",
"parking_spaces", "address", "neighborhood", "city", "state", "advertiser_name", "parking_spaces", "address", "neighborhood", "city", "state", "advertiser_name",
"advertiser_code", "creci", "latitude", "longitude", "published_at", "content_hash", "advertiser_code", "creci", "address_normalized", "neighborhood_normalized",
"creci_normalized", "latitude", "longitude", "published_at", "content_hash",
"raw_content_hash", "stable_content_hash",
"raw_html_path", "extra_json" "raw_html_path", "extra_json"
] ]
values = [data.get(c) for c in cols] values = [data.get(c) for c in cols]
@ -300,24 +412,64 @@ class Database:
snapshot = {k: v for k, v in data.items() if k not in {"raw_html_path"}} snapshot = {k: v for k, v in data.items() if k not in {"raw_html_path"}}
self.conn.execute( self.conn.execute(
"INSERT OR IGNORE INTO listing_history(listing_id,captured_at,content_hash,data_json) VALUES(?,?,?,?)", "INSERT OR IGNORE INTO listing_history(listing_id,captured_at,content_hash,data_json) VALUES(?,?,?,?)",
(data["listing_id"], ts, data["content_hash"], json.dumps(snapshot, ensure_ascii=False)), (data["listing_id"], ts, data["stable_content_hash"], json.dumps(snapshot, ensure_ascii=False)),
)
tracked = [
"url", "title", "description", "transaction_type", "property_type", "price_brl",
"condominium_brl", "iptu_brl", "area_m2", "bedrooms", "suites", "parking_spaces",
"address_normalized", "neighborhood_normalized", "advertiser_name", "creci_normalized",
"latitude", "longitude", "published_at",
]
changes = (
{key: {"before": existing[key], "after": data.get(key)} for key in tracked if existing[key] != data.get(key)}
if existing else {"listing": {"before": None, "after": "created"}}
)
event_type = "new_listing" if not existing else ("price_change" if "price_brl" in changes else "content_change")
self.conn.execute(
"""INSERT INTO listing_change_events
(listing_id,captured_at,event_type,changes_json,stable_content_hash,run_id)
VALUES(?,?,?,?,?,?)""",
(data["listing_id"], ts, event_type, json.dumps(changes, ensure_ascii=False), data["stable_content_hash"], run_id),
) )
self._record_automatic_quality_issues(data, ts)
self.conn.commit() self.conn.commit()
return changed return changed
def _record_automatic_quality_issues(self, data: dict[str, Any], ts: str) -> None:
checks: list[tuple[str, str, str]] = []
if data.get("price_brl") and data["price_brl"] > 20_000_000:
checks.append(("implausible_price", "high", str(data["price_brl"])))
if data.get("area_m2") and data["area_m2"] < 10:
checks.append(("implausible_area", "high", str(data["area_m2"])))
description = (data.get("description") or "").lower()
if "procuro casa para comprar" in description:
checks.append(("source_mismatch_buyer_request", "high", "description declares a purchase request"))
if "quarto adaptado" in description or "escritório adaptado" in description:
checks.append(("adapted_room", "medium", "description mentions an adapted room"))
for code, severity, evidence in checks:
self.conn.execute(
"""INSERT INTO data_quality_issues(listing_id,issue_code,severity,evidence,first_seen_at,last_seen_at)
VALUES(?,?,?,?,?,?) ON CONFLICT(listing_id,issue_code) DO UPDATE SET
severity=excluded.severity,evidence=excluded.evidence,last_seen_at=excluded.last_seen_at,status='open'""",
(data["listing_id"], code, severity, evidence, ts, ts),
)
def upsert_photo(self, listing_id: str, ordinal: int, url: str, **meta: Any) -> None: def upsert_photo(self, listing_id: str, ordinal: int, url: str, **meta: Any) -> None:
ts = now_utc() ts = now_utc()
self.conn.execute( self.conn.execute(
""" """
INSERT INTO photos(listing_id,ordinal,source_url,sha256,mime_type,bytes,local_path,first_seen_at,last_seen_at) INSERT INTO photos(listing_id,ordinal,source_url,sha256,mime_type,bytes,local_path,first_seen_at,last_seen_at,discovery_source,is_primary_gallery,review_status)
VALUES(?,?,?,?,?,?,?,?,?) VALUES(?,?,?,?,?,?,?,?,?,?,?,?)
ON CONFLICT(listing_id,source_url) DO UPDATE SET ON CONFLICT(listing_id,source_url) DO UPDATE SET
ordinal=excluded.ordinal, sha256=COALESCE(excluded.sha256,photos.sha256), ordinal=excluded.ordinal, sha256=COALESCE(excluded.sha256,photos.sha256),
mime_type=COALESCE(excluded.mime_type,photos.mime_type), mime_type=COALESCE(excluded.mime_type,photos.mime_type),
bytes=COALESCE(excluded.bytes,photos.bytes), bytes=COALESCE(excluded.bytes,photos.bytes),
local_path=COALESCE(excluded.local_path,photos.local_path), last_seen_at=excluded.last_seen_at local_path=COALESCE(excluded.local_path,photos.local_path), last_seen_at=excluded.last_seen_at,
discovery_source=COALESCE(excluded.discovery_source,photos.discovery_source),
is_primary_gallery=excluded.is_primary_gallery
""", """,
(listing_id, ordinal, url, meta.get("sha256"), meta.get("mime_type"), meta.get("bytes"), meta.get("local_path"), ts, ts), (listing_id, ordinal, url, meta.get("sha256"), meta.get("mime_type"), meta.get("bytes"), meta.get("local_path"), ts, ts,
meta.get("discovery_source", "listing_id_url"), int(meta.get("is_primary_gallery", True)), meta.get("review_status", "unreviewed")),
) )
self.conn.commit() self.conn.commit()
@ -329,7 +481,9 @@ class Database:
def mark_inactive(self, search_url: str, seen_ids: set[str], run_started_at: str) -> int: def mark_inactive(self, search_url: str, seen_ids: set[str], run_started_at: str) -> int:
rows = self.conn.execute( rows = self.conn.execute(
"SELECT listing_id FROM listing_searches WHERE search_url=? AND last_seen_at < ?", """SELECT s.listing_id FROM listing_searches AS s
JOIN listings AS l USING(listing_id)
WHERE s.search_url=? AND s.last_seen_at < ? AND l.inactive_at IS NULL""",
(search_url, run_started_at), (search_url, run_started_at),
).fetchall() ).fetchall()
stale = [r[0] for r in rows if r[0] not in seen_ids] stale = [r[0] for r in rows if r[0] not in seen_ids]
@ -926,8 +1080,14 @@ class Parser:
for match in IMAGE_URL_RE.finditer(page_html.replace("\\/", "/")): for match in IMAGE_URL_RE.finditer(page_html.replace("\\/", "/")):
image_urls[html.unescape(match.group(0))] = None image_urls[html.unescape(match.group(0))] = None
# Exclude common site chrome and tiny assets based on URL hints. # The portal renders recommendations on the same page. Only URLs whose
photos = [u for u in image_urls if not re.search(r"logo|favicon|sprite|icon|avatar|banner", u, re.I)] # /fotos/<id>/ owner matches this listing belong to the primary gallery.
owner_re = re.compile(r"/fotos/(\d+)/", re.I)
photos = []
for image_url in image_urls:
owner = owner_re.search(urlparse(image_url).path)
if owner and owner.group(1) == listing_id:
photos.append(image_url)
data = { data = {
"listing_id": listing_id, "listing_id": listing_id,
@ -956,8 +1116,35 @@ class Parser:
"raw_html_path": raw_path, "raw_html_path": raw_path,
"extra_json": json.dumps({"json_ld": ld}, ensure_ascii=False), "extra_json": json.dumps({"json_ld": ld}, ensure_ascii=False),
} }
stable = {k: v for k, v in data.items() if k not in {"content_hash", "raw_html_path"}} data["address_normalized"] = normalize_listing_address(data["url"], data["address"])
data["content_hash"] = hashlib.sha256(json.dumps(stable, sort_keys=True, ensure_ascii=False).encode()).hexdigest() data["neighborhood_normalized"] = "Jardins Mangueiral" if "jardins-mangueiral" in data["url"] else clean_text(data["neighborhood"])
data["creci_normalized"] = normalize_creci(data["creci"])
raw_payload = {k: v for k, v in data.items() if k not in {"content_hash", "raw_html_path"}}
data["raw_content_hash"] = hashlib.sha256(json.dumps(raw_payload, sort_keys=True, ensure_ascii=False).encode()).hexdigest()
stable_payload = {
"url": data["url"],
"title": data["title"],
"description": normalize_description_for_hash(data["description"]),
"transaction_type": data["transaction_type"],
"property_type": data["property_type"],
"price_brl": data["price_brl"],
"condominium_brl": data["condominium_brl"],
"iptu_brl": data["iptu_brl"],
"area_m2": data["area_m2"],
"bedrooms": data["bedrooms"],
"suites": data["suites"],
"parking_spaces": data["parking_spaces"],
"address_normalized": data["address_normalized"],
"neighborhood_normalized": data["neighborhood_normalized"],
"advertiser_name": data["advertiser_name"],
"creci_normalized": data["creci_normalized"],
"latitude": data["latitude"],
"longitude": data["longitude"],
"published_at": data["published_at"],
}
data["stable_content_hash"] = hashlib.sha256(json.dumps(stable_payload, sort_keys=True, ensure_ascii=False).encode()).hexdigest()
# Retain the legacy column for compatibility, but give it the stable meaning from schema v2 onward.
data["content_hash"] = data["stable_content_hash"]
return data, photos return data, photos
@ -1007,7 +1194,7 @@ class Scraper:
page_html, body, final_url = await self.fetcher.get_html(url) page_html, body, final_url = await self.fetcher.get_html(url)
raw_path = self.save_raw("listing", listing_id_from_url(url) or "unknown", body) if self.cfg.save_raw_html else None raw_path = self.save_raw("listing", listing_id_from_url(url) or "unknown", body) if self.cfg.save_raw_html else None
data, photos = Parser.detail(final_url, page_html, raw_path) data, photos = Parser.detail(final_url, page_html, raw_path)
changed = self.db.upsert_listing(data, search_url) changed = self.db.upsert_listing(data, search_url, self.run_id)
self.stats["details_ok"] += 1 self.stats["details_ok"] += 1
self.stats["changed"] += int(changed) self.stats["changed"] += int(changed)
logging.info("Listing %s: %s; %d photos", data["listing_id"], "changed" if changed else "unchanged", len(photos)) logging.info("Listing %s: %s; %d photos", data["listing_id"], "changed" if changed else "unchanged", len(photos))
@ -1049,7 +1236,18 @@ class Scraper:
logging.exception("Failed photo for %s: %s", listing_id, url) logging.exception("Failed photo for %s: %s", listing_id, url)
async def run(self) -> dict[str, Any]: async def run(self) -> dict[str, Any]:
run_id = self.db.start_run() run_config = {
"max_pages": self.cfg.max_pages,
"concurrency": self.cfg.concurrency,
"photo_concurrency": self.cfg.photo_concurrency,
"download_photos": self.cfg.download_photos,
"save_raw_html": self.cfg.save_raw_html,
"use_playwright": self.cfg.use_playwright,
"browser_channel": self.cfg.browser_channel,
"attach_chrome": self.cfg.attach_chrome,
}
run_id = self.db.start_run(self.cfg.searches, run_config)
self.run_id = run_id
status = "ok" status = "ok"
try: try:
for search_url in self.cfg.searches: for search_url in self.cfg.searches:

97
docs/ACTUAL_SCHEMA.md

@ -1,23 +1,24 @@
# Esquema físico do banco `dfimoveis.sqlite3` # Esquema físico do banco `dfimoveis.sqlite3`
> **Atualização de 22/07/2026:** as seções históricas deste documento descrevem o schema v1. A migração aditiva v2 descrita na seção final prevalece para consultas atuais.
## 1. Escopo e identificação do snapshot ## 1. Escopo e identificação do snapshot
Este documento descreve o esquema observado em 13 de julho de 2026. Ele não altera o modelo conceitual de `docs/DATA_MODEL.md` e não atribui significado não comprovado às colunas. Este documento descreve o esquema observado e revalidado em 21 de julho de 2026. Ele não altera o modelo conceitual de `docs/DATA_MODEL.md` e não atribui significado não comprovado às colunas.
| Item | Valor | | Item | Valor |
|---|---| |---|---|
| Caminho | `dfimoveis_data/dfimoveis.sqlite3` | | Caminho | `dfimoveis_data/dfimoveis.sqlite3` |
| Tamanho | 5.017.600 bytes | | Tamanho | 9.478.144 bytes |
| SHA-256 | `1ce3128b488c50c993d7328ac6e24b5905f083a7e04e3f636407eea1edd02b9d` | | SHA-256 | `bfbbdd09e88f76afd917ebd981b848b9321aeb5fc0602f712645d33e1a5ea76c` |
| Modificação do arquivo | `2026-07-13 14:20:34.601285975 -03:00` | | Data de referência | `2026-07-21` |
| SQLite | 3.45.1 |
| Codificação | UTF-8 | | Codificação | UTF-8 |
| `journal_mode` persistido | WAL | | `journal_mode` persistido | WAL |
| Páginas | 1.225 páginas de 4.096 bytes; `freelist_count = 0` | | Páginas | 2.314 páginas de 4.096 bytes; `freelist_count = 2` |
| `user_version` / `application_id` | 0 / 0 | | `user_version` / `application_id` | 0 / 0 |
| Método de abertura | `sqlite3 -readonly` e URI `mode=ro&immutable=1`, sempre com `PRAGMA query_only = ON` | | Método de abertura | URI `mode=ro`, `PRAGMA query_only = ON` e transação de leitura |
Não havia arquivo `-wal` ou `-shm` nem processo com o banco aberto na verificação inicial. Isso permitiu tratar o conteúdo como snapshot imutável. `PRAGMA quick_check` retornou `ok` e `PRAGMA foreign_key_check` não retornou violações. Na validação final, o cliente SQLite havia criado os sidecars de runtime `dfimoveis.sqlite3-shm` (32.768 bytes) e `dfimoveis.sqlite3-wal` (vazio), comportamento possível ao abrir em leitura um banco configurado em WAL. Eles não foram removidos. O arquivo principal manteve tamanho, `mtime` e SHA-256 idênticos. Como este arquivo é atualizado pelo scraper, não foi usado `immutable=1`. `PRAGMA quick_check` retornou `ok` e `PRAGMA foreign_key_check` não retornou violações. Nenhum sidecar foi removido.
## 2. Visão geral ## 2. Visão geral
@ -72,21 +73,21 @@ erDiagram
| Tabela | Linhas | Interpretação física | | Tabela | Linhas | Interpretação física |
|---|---:|---| |---|---:|---|
| `crawl_runs` | 4 | Execuções do coletor | | `crawl_runs` | 20 | Execuções do coletor |
| `listings` | 156 | Identidades de anúncios e seu estado corrente | | `listings` | 161 | Identidades de anúncios e seu estado corrente |
| `listing_history` | 156 | Estados distintos dos anúncios por hash | | `listing_history` | 562 | Estados distintos dos anúncios por hash |
| `listing_searches` | 156 | Vínculos entre anúncio e URL de busca | | `listing_searches` | 161 | Vínculos entre anúncio e URL de busca |
| `photos` | 5.386 | Mídias associadas diretamente aos anúncios | | `photos` | 7.363 | Mídias associadas diretamente aos anúncios |
| `sqlite_sequence` | 2 | Sequências internas de tabelas `AUTOINCREMENT` | | `sqlite_sequence` | 2 | Sequências internas de tabelas `AUTOINCREMENT` |
No snapshot avaliado: No snapshot avaliado:
- cada anúncio possui exatamente um registro em `listing_history`;
- cada anúncio está ligado a exatamente uma URL de busca; - cada anúncio está ligado a exatamente uma URL de busca;
- cada anúncio possui de 5 a 61 linhas em `photos`; - cada anúncio possui de 1 a 4 registros em `listing_history`;
- cada anúncio possui de 5 a 79 linhas em `photos`;
- não há órfãos nas três relações com `listings`; - não há órfãos nas três relações com `listings`;
- os 156 hashes de `listing_history` coincidem com o hash corrente em `listings`; - o hash corrente de cada anúncio possui registro correspondente no histórico;
- `captured_at` coincide com `first_seen_at` nos 156 anúncios. - grande parte dos snapshots adicionais reflete instabilidade de campos contaminados, sobretudo `creci`, e não mudança imobiliária real.
## 3. Dicionário de tabelas ## 3. Dicionário de tabelas
@ -99,10 +100,10 @@ Representa execuções técnicas do coletor. Não registra fonte, versão do scr
| `id` | INTEGER | Não | PK, `AUTOINCREMENT` | Identificador da execução; a PK inteira atribui um valor quando omitido | | `id` | INTEGER | Não | PK, `AUTOINCREMENT` | Identificador da execução; a PK inteira atribui um valor quando omitido |
| `started_at` | TEXT | Não | — | Início em ISO 8601, observado em UTC | | `started_at` | TEXT | Não | — | Início em ISO 8601, observado em UTC |
| `finished_at` | TEXT | Sim | — | Término em ISO 8601 | | `finished_at` | TEXT | Sim | — | Término em ISO 8601 |
| `status` | TEXT | Não | — | Estado textual da execução; somente `ok` na amostra | | `status` | TEXT | Não | — | Estado textual da execução; `ok` e `failed` foram observados |
| `stats_json` | TEXT | Não | `'{}'` | Contadores técnicos em JSON | | `stats_json` | TEXT | Não | `'{}'` | Contadores técnicos em JSON |
As quatro estruturas JSON são válidas. As chaves presentes são `search_pages`, `listing_urls`, `details_ok`, `details_failed`, `changed`, `photos_ok`, `photos_failed` e `inactive`, todas inteiras. As estruturas JSON observadas são válidas. Além das chaves originais, execuções recentes também registram `photos_skipped`.
### 3.2 `listings` ### 3.2 `listings`
@ -219,20 +220,64 @@ As FKs de `listing_history`, `listing_searches` e `photos` apontam para `listing
1. `listing_id` identifica um **anúncio no portal**, não um imóvel físico. 1. `listing_id` identifica um **anúncio no portal**, não um imóvel físico.
2. Uma linha de `listing_history` é um **estado de conteúdo distinto**, não prova de que o anúncio foi observado somente uma vez. Observações idênticas são condensadas em `last_seen_at`. 2. Uma linha de `listing_history` é um **estado de conteúdo distinto**, não prova de que o anúncio foi observado somente uma vez. Observações idênticas são condensadas em `last_seen_at`.
3. O banco ainda não contém histórico suficiente para preços ou permanência: há um snapshot por anúncio e todos os anúncios têm `first_seen_at = last_seen_at`. 3. O banco ainda não contém histórico suficiente para liquidez: há quatro dias efetivos de observação e a maioria das transições é ruído de campos contaminados.
4. Anúncios diferentes com preço, área ou fotos semelhantes permanecem anúncios distintos. Não há chave de endereço/unidade nem tabela de candidatos físicos. 4. Anúncios diferentes com preço, área ou fotos semelhantes permanecem anúncios distintos. Não há chave de endereço/unidade nem tabela de candidatos físicos.
5. `area_m2` não deve ser usada para preço por m² até que o conceito de área seja validado por tipologia e fonte. 5. `area_m2` não deve ser usada para preço por m² até que o conceito de área seja validado por tipologia e fonte.
6. `bedrooms` é a quantidade anunciada. Ela não comprova a planta original de três quartos exigida para Jardins Mangueiral. 6. `bedrooms` é a quantidade anunciada. Ela não comprova a planta original de três quartos exigida para Jardins Mangueiral.
7. Datas com sufixo `+00:00` são tratadas como UTC. A cobertura observada é de um único dia. 7. Datas com sufixo `+00:00` são tratadas como UTC. A cobertura observada vai de 13 a 21/07/2026.
8. Os campos textuais contaminados devem ser reextraídos do HTML bruto em uma camada derivada; os valores atuais não devem ser silenciosamente normalizados como se fossem corretos. 8. Os campos textuais contaminados devem ser reextraídos do HTML bruto em uma camada derivada; os valores atuais não devem ser silenciosamente normalizados como se fossem corretos.
## 7. Problemas conhecidos do snapshot ## 7. Problemas conhecidos do snapshot
- 146 de 156 endereços têm mais de 300 caracteres; 154 de 156 bairros têm mais de 100 caracteres. Os dois bairros curtos também não formam valores confiáveis. - 150 de 161 endereços têm mais de 300 caracteres; 159 de 161 bairros têm mais de 100 caracteres.
- `city`, `state`, `latitude`, `longitude`, `published_at` e `advertiser_name` estão integralmente ausentes. - `city`, `state`, `latitude`, `longitude`, `published_at` e `advertiser_name` estão integralmente ausentes.
- 98 códigos de anunciante e 153 valores de CRECI são longos demais para o significado esperado, indicando extração contaminada. - 99 códigos de anunciante e 157 valores de CRECI são longos demais para o significado esperado, indicando extração contaminada.
- Há um preço de R$ 570.000.000 e uma área de 0,11 m², ambos candidatos fortes a erro de extração/escala. - Há um preço de R$ 570.000.000 e uma área de 0,11 m², ambos candidatos fortes a erro de extração/escala.
- O JSON-LD reservado em `extra_json` é um array vazio em todos os anúncios e não oferece uma fonte estruturada alternativa no snapshot. - O JSON-LD reservado em `extra_json` é um array vazio em todos os 161 anúncios e não oferece uma fonte estruturada alternativa no snapshot.
- Três downloads de foto falharam e deixaram linhas sem hash, MIME, tamanho e caminho local. - As 7.363 linhas de foto têm hash e caminho local preenchidos; isso não garante que toda imagem pertença ao imóvel.
- Hashes de foto idênticos aparecem em muitos anúncios, inclusive um hash presente nos 156; isso demonstra a presença de ativos genéricos e impede usar igualdade de hash como prova isolada de imóvel duplicado. - Hashes de foto idênticos aparecem em muitos anúncios; isso demonstra a presença de ativos genéricos e impede usar igualdade de hash como prova isolada de imóvel duplicado.
- Das 401 transições históricas, 398 alteram `creci`; somente três anúncios apresentam mudança real de preço. O histórico precisa de normalização antes de análises temporais.
- Não há como ligar formalmente anúncio/snapshot a `crawl_runs`, nem distinguir por chave a fonte se uma segunda fonte for incorporada. - Não há como ligar formalmente anúncio/snapshot a `crawl_runs`, nem distinguir por chave a fonte se uma segunda fonte for incorporada.
---
## 12. Schema v2 — 22/07/2026
A migração foi autorizada explicitamente, executada após backup consistente e não removeu linhas ou colunas antigas.
Contagens preservadas na migração:
| Tabela legada | Linhas |
|---|---:|
| `listings` | 162 |
| `listing_searches` | 162 |
| `listing_history` | 637 |
| `photos` | 7.457 |
| `crawl_runs` | 21 |
Novas colunas:
- `listings`: `address_normalized`, `neighborhood_normalized`, `creci_normalized`, `raw_content_hash`, `stable_content_hash`;
- `photos`: `discovery_source`, `is_primary_gallery`, `review_status`;
- `crawl_runs`: `searches_json`, `config_json`, `scope_complete`.
Novas tabelas:
- `availability_observations`: evidência datada e sua fonte, sem converter selo visual em prova registral;
- `data_quality_issues`: quarentenas reversíveis, com severidade, evidência e estado;
- `listing_change_events`: eventos materiais produzidos pelo hash estável nas futuras coletas.
Novas visões:
- `v_primary_photos`;
- `v_open_data_quality_issues`;
- `v_listing_current_availability`.
Auditoria inicial:
- 3.521 fotos pertencem à galeria do próprio anúncio;
- 3.286 registros legados apontam para `/fotos/<outro_listing_id>/` e vieram de recomendações do portal;
- 650 registros restantes são ativos visuais do site ou URLs sem proprietário de anúncio;
- os 3.936 registros não primários foram preservados e marcados, não apagados;
- 15 anúncios foram normalizados para o CRECI `26903`;
- `PRAGMA quick_check` retornou `ok` após a migração.

610
docs/ANALYSIS_RULES.md

@ -1,490 +1,283 @@
# Regras de análise # Regras de análise
## 1. Objetivo metodológico ## 1. Unidade de análise
Produzir análises reproduzíveis e úteis para decisão de compra, sem transformar dados de anúncios em uma falsa precisão de mercado. Definir explicitamente:
Toda conclusão deve distinguir: - anúncio;
- observação;
- o que os dados mostram;
- o que foi inferido;
- o que permanece desconhecido;
- o que depende de visita, documentação ou negociação.
---
## 2. Unidade de análise
Definir antes de calcular:
- anúncio único;
- snapshot de anúncio;
- imóvel físico provável; - imóvel físico provável;
- imóvel físico confirmado;
- região; - região;
- tipologia; - período;
- período. - snapshot.
Não misturar unidades.
Exemplo de erro:
> Contar cada snapshot mensal como um imóvel diferente.
Exemplo correto:
> Para estoque histórico, usar um anúncio ativo por data; para oferta única, deduplicar anúncios e imóveis prováveis. Não contar cada observação como imóvel novo.
--- ---
## 3. Cobertura e consistência temporal ## 2. Cobertura
Antes de comparar meses:
- verificar dias coletados; Antes de comparar períodos:
- verificar fontes ativas;
- verificar falhas;
- verificar mudanças do scraper;
- verificar novos filtros;
- verificar alterações no site;
- verificar interrupções;
- verificar mudanças de campos.
Uma queda no número de anúncios pode ser falha de coleta. - conferir dias coletados;
- falhas e 403;
- fontes e pesquisas ativas;
- mudanças do scraper;
- cobertura de fotos;
- inatividade artificial.
Relatórios temporais devem exibir cobertura. O snapshot de 13–21/07/2026 não permite inferir liquidez.
--- ---
## 4. Limpeza ## 3. Limpeza
### 4.1 Preços inválidos ### Preço
Identificar: Colocar em quarentena:
- zero; - zero;
- preço mensal; - preço de aluguel em venda;
- preço de aluguel em anúncio de venda;
- valor parcial;
- erro de escala; - erro de escala;
- preço simbólico; - valor simbólico;
- preço por fração; - valor incompatível com descrição;
- moeda incorreta; - casos como R$ 570 milhões quando o contexto sugere erro.
- preço inconsistente com descrição.
Não remover silenciosamente. Registrar regra e contagem. ### Área
### 4.2 Áreas inválidas Verificar:
Identificar: - conceito da área;
- casa ampliada;
- terreno versus área construída;
- erro de decimal;
- divergência entre campos e descrição.
- zero; ### Planta
- área total usada como privativa;
- área do terreno;
- área multiplicada;
- vírgula decimal interpretada incorretamente;
- área do condomínio;
- área incompatível com a tipologia.
### 4.3 Quartos e tipologia
Verificar divergências entre: Cruzar:
- título; - título;
- campos estruturados; - campos estruturados;
- descrição; - descrição;
- planta; - fotos;
- fotografias; - planta padrão;
- tipologia original. - endereço e implantação.
Para Jardins Mangueiral, não aceitar tipologia anunciada como prova da planta original.
--- ---
## 5. Preço por metro quadrado ## 4. Preço por metro quadrado
Calcular somente quando:
- o preço é válido;
- a área é válida;
- a área representa conceito comparável;
- o imóvel pertence a uma tipologia comparável.
Registrar a área usada.
Não comparar diretamente: Não usar como métrica principal no estado atual do banco.
- área privativa com área total; Somente calcular quando:
- casa com apartamento;
- imóvel reformado com imóvel para reforma sem controle;
- dois quartos com três quartos apenas pela média regional;
- cobertura com unidade padrão;
- casa ampliada com planta original sem ajustar a área.
Estatísticas mínimas: - preço é válido;
- área é validada e comparável;
- tipologia é homogênea;
- ampliação é tratada corretamente.
- número de observações; Os rankings de 21/07/2026 não usaram preço por m² porque os campos de área não eram uniformes.
- mediana;
- percentil 25;
- percentil 75;
- mínimo e máximo após validação;
- proporção excluída.
--- ---
## 6. Outliers ## 5. Fotografias
Não excluir outliers apenas porque são extremos. A avaliação deve incluir:
Investigar se representam: - primeira foto;
- galeria completa;
- erro; - coerência de planta;
- imóvel premium; - condição aparente;
- reforma; - iluminação e ventilação;
- venda urgente; - qualidade da reforma;
- leilão; - ampliação e cobertura;
- área incorreta; - duplicatas visuais;
- anúncio duplicado; - selo de vendido.
- preço desatualizado;
- unidade atípica.
Apresentar resultados com e sem outliers quando a conclusão mudar. Registrar limitações quando a galeria for curta ou não mostrar toda a planta.
Métodos permitidos: ---
- IQR; ## 6. Duplicatas
- MAD;
- limites específicos por tipologia;
- inspeção manual;
- modelos robustos.
Não usar corte automático sem explicar. - identificar grupos de republicação;
- contar como um imóvel provável no ranking;
- preservar todos os anúncios no histórico;
- definir anúncio de referência;
- registrar divergências de endereço, área e preço;
- testar sensibilidade quando a deduplicação for incerta.
--- ---
## 7. Duplicidades ## 7. Ranking de imóveis
O ranking serve para ordenar investigação.
Relatar: Critérios comuns:
- duplicatas dentro da mesma fonte; - quartos utilizáveis;
- duplicatas entre fontes; - planta;
- snapshots repetidos; - funcionalidade;
- imóveis anunciados por vários corretores; - condição aparente;
- anúncios republicados com novo identificador. - iluminação e ventilação;
- localização;
- acessibilidade;
- elevador;
- garagem;
- custo;
- regularidade;
- qualidade da evidência.
Para preço de oferta atual, escolher uma regra explícita, por exemplo: ### Mangueiral
- menor preço vigente por imóvel provável; Adicionar:
- anúncio mais recente;
- anúncio primário;
- mediana entre anúncios do mesmo imóvel.
Testar sensibilidade da conclusão à regra. - planta original de três quartos;
- QC;
- conversão do quintal;
- calor e ventilação;
- cobertura, drenagem e impermeabilização;
- regularidade da ampliação;
- ruído de vizinhos e áreas comuns.
Aplicar também as preferências confirmadas:
- favorecer cozinha funcional e gourmet conectado ao uso cotidiano;
- favorecer conservação e mudança com poucas intervenções;
- tratar piscina, jacuzzi e banheira como incompatibilidades funcionais, não amenidades positivas;
- penalizar cubas de vidro, marcenaria degradada, gourmet isolado e área externa sem uso coerente;
- testar calor e ventilação em fechamentos envidraçados;
- distinguir nota dada diretamente pelo usuário de aderência inferida pelo analista;
- quando novo feedback alterar o funil de entrada, reprocessar o universo completo em vez de apenas reordenar a shortlist existente.
--- ### Cruzeiro
## 8. Histórico de anúncios Adicionar:
Para cada anúncio ou imóvel provável, tentar identificar: - ranking interno da quadra;
- andar;
- elevador;
- garagem;
- posição do bloco;
- sistemas do prédio;
- reforma real versus estética.
- primeira aparição; Não criar pesos secretos. Se houver pontuação, documentar pesos e sensibilidade.
- última aparição;
- tempo observado;
- mudanças de preço;
- períodos de ausência;
- republicação;
- alteração de corretor;
- alteração de descrição;
- alteração de área ou quartos.
Não concluir “vendido” apenas porque desapareceu. ---
Estados possíveis: ## 8. Outliers
- ativo; Investigar antes de excluir:
- ausente na coleta;
- removido;
- republicado;
- provável venda;
- status desconhecido.
“Provável venda” exige sinais adicionais. - erro;
- imóvel premium;
- reforma;
- venda urgente;
- leilão;
- área incorreta;
- duplicata;
- preço desatualizado.
Apresentar resultados com e sem outliers quando a conclusão mudar.
--- ---
## 9. Comparáveis ## 9. Comparáveis
Um conjunto de comparáveis deve controlar, quando possível: Controlar, quando possível:
- região; - região e micro-localização;
- quadra, bloco ou QC; - tipo;
- tipo de imóvel;
- planta original; - planta original;
- quartos; - quartos;
- área; - área validada;
- garagem; - garagem;
- elevador; - elevador;
- andar; - andar;
- posição; - estado;
- reforma; - ampliação;
- conservação;
- data; - data;
- condomínio; - condomínio;
- situação de ocupação. - ocupação.
Quando não for possível controlar, declarar a limitação.
Nunca usar uma média ampla do bairro como avaliação definitiva de uma unidade específica. Média ampla do bairro não define preço justo de unidade específica.
--- ---
## 10. Tendências ## 10. Tendência e liquidez
Para avaliar tendência de preço: Para tendência:
- usar imóveis comparáveis; - aumentar a janela histórica;
- controlar mudanças no mix; - controlar mudança de mix;
- ponderar cobertura; - usar mediana e métodos robustos;
- evitar média simples; - separar entrada, preço atual e último preço;
- usar mediana e regressões robustas quando necessário; - mostrar dispersão.
- separar preço de entrada, preço atual e último preço;
- mostrar intervalo de confiança ou dispersão;
- evitar extrapolação longa.
Uma alta no preço mediano pode ocorrer porque imóveis menores desapareceram da amostra. Para liquidez:
---
## 11. Estoque e liquidez - entradas e saídas;
Métricas úteis:
- anúncios ativos;
- imóveis prováveis ativos;
- entradas;
- saídas;
- republicações;
- tempo observado; - tempo observado;
- republicações;
- reduções de preço; - reduções de preço;
- percentual com múltiplas reduções; - anúncios ativos;
- concentração por corretor; - imóveis prováveis ativos.
- dispersão de preços;
- taxa de reposição do estoque.
Limitações:
- tempo observado não é tempo real no mercado quando a coleta começou depois;
- retirada não confirma venda;
- republicação pode reiniciar o relógio artificialmente.
---
## 12. Análise financeira
### 12.1 Premissas obrigatórias
Toda simulação deve declarar:
- preço;
- entrada em dinheiro;
- FGTS;
- reserva preservada;
- custos de aquisição;
- reforma;
- valor financiado;
- taxa efetiva;
- CET, se disponível;
- sistema;
- prazo;
- seguros e tarifas;
- condomínio;
- IPTU;
- manutenção;
- reajuste do aluguel;
- retorno líquido dos investimentos;
- inflação;
- valorização;
- horizonte;
- custos de venda;
- estratégia de amortização.
### 12.2 Liquidez
Separar:
- dinheiro;
- investimentos com liquidez;
- FGTS;
- patrimônio imobiliário;
- reserva mínima.
Não tratar FGTS como reserva de emergência.
### 12.3 Compra versus aluguel
Comparar:
- fluxo mensal;
- patrimônio líquido;
- custo de oportunidade;
- risco;
- liquidez;
- flexibilidade;
- estabilidade;
- manutenção;
- impostos;
- custos de transação.
Mostrar cenários:
- conservador;
- central;
- otimista.
Não decidir apenas pelo patrimônio final médio.
### 12.4 Estresse
Testar:
- juros maiores;
- renda menor;
- despesas extraordinárias;
- reforma acima do orçamento;
- vacância ou dificuldade de revenda;
- valorização baixa;
- investimento com retorno menor;
- manutenção elevada;
- redução da capacidade de poupança.
---
## 13. Consórcio Retirada não confirma venda.
Para analisar um grupo:
- usar histórico do próprio grupo;
- registrar assembleias;
- lances vencedores;
- lance livre;
- lance fixo;
- lance embutido;
- número de contemplações;
- saldo;
- reajuste da carta;
- taxa de administração;
- fundo de reserva;
- seguros;
- regras de uso;
- prazo;
- valor total pago.
Não inferir probabilidade de contemplação usando apenas média histórica.
Analisar:
- distribuição;
- percentis;
- sazonalidade;
- número de concorrentes;
- mudanças de regra;
- eventos de renda;
- concentração de lances;
- sensibilidade ao valor ofertado.
Relatos de gerente são hipóteses a testar.
--- ---
## 14. Relatórios por região ## 11. Integração financeira
### Cruzeiro
Separar por: Não recomendar imóvel apenas pelo ranking físico.
- Cruzeiro Novo e Velho; Calcular:
- quadra;
- bloco;
- tipologia;
- área;
- elevador;
- garagem;
- andar;
- conservação;
- reforma.
### Jardins Mangueiral - preço provável negociado;
- custos de compra;
- reforma e regularização;
- entrada e FGTS;
- reserva remanescente;
- financiamento e CET;
- prestação inicial;
- custo mensal total;
- cenário de amortização;
- comparação com aluguel.
Separar por: A classificação final pode ser reduzida se o imóvel exigir consumir a reserva.
- QC;
- planta original de três quartos;
- área construída;
- ampliação;
- qualidade da área fechada;
- condomínio;
- posição;
- ruído;
- proximidade de serviços;
- padrão de reforma.
Excluir casas originalmente de dois quartos.
--- ---
## 15. Relatório padrão ## 12. Saída padrão
### Resumo executivo Cada ficha deve conter:
- conclusão principal; - anúncio e URL;
- confiança; - snapshot;
- implicação prática; - localização;
- maior risco. - tipologia original provável;
- preço e histórico;
### Dados - áreas e confiabilidade;
- quartos, vagas, elevador e andar;
- fontes; - possíveis duplicatas;
- período; - status visual;
- cobertura; - pontos fortes;
- tamanho da amostra; - pontos fracos;
- exclusões; - custos e riscos;
- deduplicação. - pendências de visita e documentação;
- posição relativa;
### Resultados - próxima ação.
- tabelas;
- distribuições;
- comparáveis;
- tendências;
- sensibilidade.
### Interpretação
- o que é sustentado;
- o que é apenas sugestivo;
- o que requer visita;
- o que requer confirmação financeira ou documental.
### Próxima ação
Classificar como:
- ignorar;
- monitorar;
- aprofundar;
- visitar;
- negociar;
- candidato forte;
- decisão insuficiente.
--- ---
## 16. Reprodutibilidade ## 13. Reprodutibilidade
Para cada relatório, salvar: Estrutura recomendada:
```text ```text
reports/<nome>/ reports/<analise>/
├── README.md ├── README.md
├── query.sql ├── query.sql
├── analysis.py ├── analysis.py
@ -493,50 +286,25 @@ reports/<nome>/
└── figures/ └── figures/
``` ```
Registrar: Registrar snapshot, hash do código, filtros, contagens e julgamento manual.
- data;
- hash do código;
- identificação do banco;
- filtros;
- versão;
- ambiente;
- contagens.
--- ---
## 17. Linguagem das conclusões ## 14. Linguagem
Preferir: Preferir:
- “os anúncios observados sugerem”; - “na amostra observada”;
- “na amostra coletada”;
- “após deduplicação”; - “após deduplicação”;
- “não há evidência suficiente”; - “as fotos sugerem”;
- “a conclusão é sensível a”; - “exige confirmação”;
- “o dado não permite afirmar”. - “não há série suficiente”;
- “a conclusão é sensível a”.
Evitar: Evitar:
- “o mercado vale”;
- “foi vendido” sem prova; - “foi vendido” sem prova;
- “esta é a melhor quadra” sem critério; - “preço justo” sem intervalo;
- “o preço justo é” sem intervalo e comparáveis; - “vai valorizar”;
- “vai valorizar” como certeza; - “melhor quadra” sem critério;
- “o lance será contemplado” como previsão. - “contemplação provável” sem dados do grupo.
---
## 18. Critério final
Uma análise é boa quando ajuda a evitar:
- comprar cedo demais;
- pagar por uma reforma disfarçada;
- confundir anúncio com transação;
- superestimar valorização;
- subestimar custos;
- comprometer liquidez;
- aceitar uma planta inadequada;
- tomar decisão por ansiedade;
- descartar um bom imóvel por uma métrica superficial.

90
docs/CONTEXT_CHANGELOG.md

@ -0,0 +1,90 @@
# Histórico de mudanças do contexto
## 22/07/2026 — versão 9
- relatório Top 15 do Mangueiral refeito isoladamente sobre a execução 22 e o schema v2;
- snapshot consolidado em 95 anúncios do Mangueiral, 78 ativos e 17 inativos; banco com 162 anúncios, 638 observações e 7.457 registros de foto;
- análise fotográfica passou a admitir somente os 3.521 registros da galeria primária;
- teto eliminatório de R$ 750 mil aplicado ao ranking, removendo `1352633` (R$ 755 mil);
- `1401074`, QC 13 e R$ 720 mil, entrou em 8º após revisão da primeira foto, descrição e galeria completa; aderência permanece inferida e pendente de avaliação do usuário;
- `1290512` registrado como inativo sem inferência de venda;
- `348115` atualizado de R$ 530 mil para R$ 550 mil conforme evento material registrado na execução 22.
## 22/07/2026 — versão 8
- confirmado pelo usuário o preço máximo tolerado de **R$ 750.000** para o imóvel;
- registrado que esse valor é teto, não meta de gasto, e não inclui custos de compra, mudança, reforma ou regularização;
- preservada a exigência de ao menos R$ 50.000 de reserva líquida após todos os desembolsos.
## 22/07/2026 — versão 7
- primeira execução após a migração validada: 78 detalhes, zero falhas e apenas uma mudança material (`348115`, de R$ 530 mil para R$ 550 mil);
- captura restrita às galerias primárias reutilizou 1.854 fotos e não adicionou recomendações do portal;
- corrigido o contador `inactive` para contar apenas novas inativações, não repetir todo o estoque já inativo;
- execução 21 concluída somente para o Mangueiral: 95 acumulados, 78 ativos e 17 inativos;
- novo anúncio `1401074`; `1290512` inativado sem inferência de venda; `1346118` alterado de R$ 530 mil para R$ 550 mil;
- criado backup consistente anterior à migração v2;
- schema migrado de forma aditiva, preservando integralmente anúncios, histórico, fotos e execuções;
- adicionadas normalização de CRECI/localização, hash estável, escopo de execução, disponibilidade, eventos materiais e quarentenas;
- identificados 3.936 registros legados fora da galeria principal; 3.286 apontam para fotos de outros IDs e o restante para ativos do portal;
- relatórios analíticos passaram a usar somente `is_primary_gallery=1`.
## 21/07/2026 — versão 6
Revisão integral após a execução 20 do scraper.
Mudanças:
- snapshot atualizado para 161 anúncios, 562 observações e 7.363 fotos;
- Mangueiral consolidado em 94 anúncios acumulados, 78 ativos e 16 inativos;
- Cruzeiro consolidado em 67 anúncios acumulados, 63 ativos e 4 inativos;
- documentado que 398 de 401 transições históricas alteram `creci` contaminado e só três anúncios tiveram mudança real de preço;
- revalidadas dez quarentenas por selo visual de vendido, uma no Cruzeiro e nove no Mangueiral;
- `1356197` removido do Top 15 por novo selo visual e substituído por `1290502`;
- triagem de similaridade fotográfica reexecutada com Pillow 12.3.0, sem alteração dos rankings;
- recebida avaliação direta de 1 a 5 dos 15 finalistas do Mangueiral; após reprocessar todo o universo ativo, `1279892`, `1319996` e `1377683` passam a formar o Top 3, sendo a aderência do terceiro ainda inferida;
- confirmada preferência por cozinha funcional e integrada ao gourmet, conservação e baixa necessidade de intervenção;
- piscina, jacuzzi e banheira confirmadas como incompatibilidades; cubas de vidro, marcenaria ruim, gourmet isolado e fechamento envidraçado quente como fortes negativos;
- análise do Mangueiral refeita sobre os 78 anúncios ativos, evitando limitar o feedback aos 15 antigos;
- `1377683`, `1371718` e `1140317` incluídos no Top 5 por aderência inferida, explicitamente pendente de avaliação do usuário;
- auditadas as primeiras fotos dos 78 anúncios ativos; `1301400` foi incluído na quarentena visual e `1369473` foi separado por ser solicitação de compra, não oferta;
- incluídos `1381514` e `1400223` no Top 15 condicionado do Cruzeiro;
- removido `1375199` por inatividade, sem inferir venda;
- relatórios, parâmetros, resultados, shortlist, estado atual, esquema e manual operacional sincronizados com 21/07.
## 20/07/2026 — versão 5
Revisão dos demais chats do projeto e dos relatórios atuais.
Mudanças:
- consolidada a prioridade atual do Jardins Mangueiral;
- Cruzeiro definido como alternativa secundária e oportunística;
- preservado o ranking interno Ouro/Prata/Bronze do Cruzeiro;
- incluídas QCs preferenciais do Mangueiral;
- incorporado o filtro de planta original de três quartos;
- incorporada a regra de avaliar funcionalidade, não apenas perda do quintal;
- incorporada avaliação explícita de ruído de vizinhança;
- criado documento financeiro com saldos, renda, aluguel, faixa-alvo e reserva;
- Pró-Cotista e MCMV Faixa 4 marcados como excluídos;
- incorporado o cenário de consórcio BB e a decisão de não contratá-lo;
- criado manual do scraper com caminho real, Playwright e Cloudflare;
- incorporados os snapshots Top 15 de 20/07/2026;
- corrigidas contradições remanescentes no `AGENTS.md` e no contexto do projeto.
## 13/07/2026 — mudança de prioridade
A consolidação inicial tratava Cruzeiro e Jardins Mangueiral como igualmente prioritários.
Correção:
1. Jardins Mangueiral passou a ser o foco principal.
2. Cruzeiro passou a ser secundário e oportunístico.
3. O ranking interno do Cruzeiro permaneceu válido para imóveis concretos.
## 11/07/2026 — filtro do Mangueiral
- casas originalmente de dois quartos excluídas;
- somente plantas originais de três quartos;
- quintal fechado deixou de ser penalidade automática;
- conversão deve ser julgada por funcionalidade, luz, ventilação e redundância.

53
docs/CURRENT_SHORTLIST.md

@ -0,0 +1,53 @@
# Shortlist atual — snapshot de 22/07/2026
Este documento resume os relatórios completos em `reports/mangueiral_top15/`, `reports/cruzeiro_top15/` e `reports/cruzeiro_vs_mangueiral/`.
## 1. Jardins Mangueiral — prioridade principal
| # | Anúncio | QC | Preço | Razão principal | Pendência principal |
|---:|---:|---:|---:|---|---|
| 1 | 1279892 | 10 | R$ 595 mil | nota 4/5; cozinha e gourmet funcionais, poucas alterações | planta, áreas e regularização |
| 2 | 1319996 | 14 | R$ 649 mil | nota 4/5; cozinha, gourmet, quintal e conservação | reversão do closet, cobertura e sistema solar |
| 3 | 1377683 | 11 | R$ 585 mil | aderência inferida; cozinha funcional e gourmet aberto | usuário avaliar, planta, cobertura e drenagem |
| 4 | 1371718 | 14 | R$ 650 mil | aderência inferida; cozinha, conservação e gourmet aberto | usuário avaliar, sistemas e insolação |
| 5 | 1140317 | 5 | R$ 599 mil | aderência inferida; cozinha equipada e gourmet conectado | usuário avaliar, cobertura e uma vaga |
Observações:
- o imóvel `1346017`, antes em terceiro, saiu por selo visual de vendido;
- `1356197`, antes em nono, também passou a exibir “VENDIDO” e foi substituído por `1290502`, QC 11;
- outras unidades ficaram em quarentena por indicação visual de venda;
- casas fora das QCs preferenciais podem entrar quando qualidade e preço compensam;
- áreas anunciadas não foram usadas para preço por m².
- a ordem foi recalibrada com as notas e comentários diretos do usuário em 21/07;
- o funil foi então refeito sobre os 78 ativos; `1377683`, `1371718` e `1140317` são achados novos, ainda sem nota pessoal;
- piscina, jacuzzi e banheira são incompatibilidades confirmadas; cubas de vidro e necessidade de reforma antes da mudança são fortes negativos.
- o teto de R$ 750 mil excluiu `1352633`, anunciado por R$ 755 mil;
- `1401074`, QC 13 e R$ 720 mil, entrou em 8º por aderência inferida; ainda depende de avaliação pessoal e diligência documental;
- `1290512` ficou inativo em 22/07; a ausência não foi tratada como venda.
## 2. Cruzeiro Novo — alternativa secundária
| # | Anúncio | Local | Preço | Razão principal | Pendência principal |
|---:|---:|---|---:|---|---|
| 1 | 1345146 | SHCES 1207 A | R$ 670 mil | reforma coerente, suíte e elevador anunciado | vaga, matrícula e sistemas |
| 2 | 1357415 | SHCES 1109 | R$ 630 mil | reforma, suíte, nascente e vazado | confirmar elevador |
| 3 | 1357633 | SHCES 309 | R$ 600 mil | reforma uniforme e sistemas renovados declarados | elevador, garagem e obra |
| 4 | 1376958 | SHCES 911 B | R$ 599 mil | reformado, nascente, vazado e suíte | sem elevador e garagem incerta |
| 5 | 1345903 | SHCES 1303 | R$ 565 mil | primeiro andar e preço competitivo | sem garagem, ruído e segurança |
Observações:
- `1263868` foi retirado por “VENDIDO” na primeira foto;
- `1381514` e `1400223` entraram no Top 15 condicionado; nenhum supera o Top 5 atual;
- `1375199` ficou inativo em 21/07 e saiu do ranking; ausência não prova venda;
- a janela de coleta é insuficiente para liquidez;
- a posição no ranking de imóvel concreto pode superar a faixa geral da quadra;
- andar, elevador, vaga, matrícula, instalações e ruído exigem confirmação.
## 3. Uso correto
- começar visitas pelo Mangueiral;
- usar o Cruzeiro quando surgir oportunidade concreta excepcional;
- reprocessar o ranking quando o snapshot mudar;
- não carregar posições de 21/07/2026 como verdade permanente.

145
docs/CURRENT_STATE.md

@ -0,0 +1,145 @@
# Estado atual do projeto
**Data de referência:** 22/07/2026
Este é o resumo vigente. Ele prevalece sobre fases anteriores da conversa.
## 1. Decisão imobiliária
### Prioridade regional
1. **Jardins Mangueiral — foco principal.**
2. **Cruzeiro — alternativa secundária e oportunística.**
O entendimento mudou após a comparação de imóveis concretos e uma visita ao Cruzeiro. O estoque do Mangueiral passou a oferecer, com maior frequência, casas mais novas, funcionais e compatíveis com moradia de longo prazo. No Cruzeiro, apareceram mais concessões em reforma, garagem, elevador, conservação e planta.
Isso não exclui o Cruzeiro. Um apartamento excepcional pode superar uma casa mediana do Mangueiral.
### Filosofia
- Esperar pelo imóvel certo é preferível a comprar com grandes concessões.
- A decisão deve equilibrar finanças, segurança patrimonial, funcionalidade e bem-estar.
- Possuir uma casa física no longo prazo tem valor psicológico, mas não justifica comprometer liquidez ou aceitar um imóvel inadequado.
---
## 2. Filtros vigentes
### Jardins Mangueiral
- somente planta original de três quartos;
- QCs 8 e 9 preferenciais;
- QCs 6 e 7 alternativas fortes;
- QCs 10 e 11 também consideradas boas;
- posição exata pode superar a classificação da QC;
- quintal fechado é aceitável quando convertido em espaço útil;
- evitar duplicação de cozinha, perda de ventilação, calor e circulação ruim;
- avaliar ruído de paredes compartilhadas, pets, música, áreas comuns e rua.
### Cruzeiro
Ranking interno de localização:
- **Ouro:** SHCES 1501, 1503, 1505, 1203, 1205 e 1207.
- **Prata:** SHCES 1103, 1105, 1107, 1109, 1111, 1303, 1305, 1307 e 1309.
- **Bronze:** conjunto SHCES 1400.
- **Caso a caso:** 1211 e 1507.
Esse ranking é heurístico e não substitui a avaliação da unidade.
---
## 3. Referência financeira vigente
Últimos valores informados:
- renda bruta: aproximadamente R$ 18.500 por mês;
- recursos líquidos: R$ 222.000;
- FGTS: R$ 130.000;
- reserva líquida mínima pós-compra: R$ 50.000;
- capacidade de poupança: R$ 15.000 a cada seis meses;
- aluguel usado nas comparações recentes: R$ 3.200 por mês, sem condomínio e IPTU;
- faixa de imóveis-alvo: aproximadamente R$ 650.000 a R$ 750.000;
- preço máximo tolerado para o imóvel: **R$ 750.000**.
Esses números devem ser datados e confirmados antes de decisão ou simulação nova.
O teto de R$ 750.000 é um limite pessoal confirmado, não uma meta de gasto nem uma autorização para comprometer a reserva. Custos de compra, mudança, reforma e regularização devem ser considerados separadamente e ainda precisam preservar a reserva mínima.
### Exclusões
- Pró-Cotista: fora do conjunto de opções, pelas regras consideradas de renda bruta máxima de R$ 12.000 e imóvel usado até R$ 500.000.
- MCMV Faixa 4: não usar nas simulações.
- Imóveis sem matrícula/escritura/regularidade suficiente: não presumir financiamento bancário.
### Estratégia atual
- financiamento tradicional é a referência principal quando houver compra;
- consórcio pode ter custo nominal menor em alguns cenários, mas perde em previsibilidade, liquidez e risco de contemplação;
- a proposta específica de consórcio BB não foi contratada;
- preservar reserva e manter estratégia lenta é preferível a forçar a compra.
---
## 4. Banco e coleta
Caminho esperado:
```text
dfimoveis_data/dfimoveis.sqlite3
```
Snapshot estrutural de 22/07/2026:
- 162 anúncios;
- 638 observações históricas;
- 7.457 registros históricos de foto, dos quais 3.521 pertencem à galeria primária;
- Cruzeiro Novo: 67 anúncios acumulados, 63 ativos, 4 inativos;
- Jardins Mangueiral: 95 anúncios acumulados, 78 ativos, 17 inativos;
- cobertura: 13 a 22/07/2026;
- Cruzeiro não integra mais a lista de buscas; seus dados permanecem preservados com última coleta em 21/07.
Em 22/07 foi aplicada migração aditiva do schema, após backup consistente. Valores brutos e registros históricos foram preservados. Endereço, bairro e CRECI agora possuem colunas normalizadas; fotos de recomendações do portal são mantidas, mas marcadas como fora da galeria principal; disponibilidade e problemas de qualidade possuem tabelas auditáveis.
A janela é curta demais para inferir liquidez.
A maioria dos snapshots adicionais é ruído de campos textuais contaminados: 398 transições alteram `creci`, enquanto somente três anúncios tiveram mudança real de preço. Não interpretar o número bruto de observações como mudanças de mercado.
---
## 5. Rankings vigentes
Os rankings completos estão em:
- `reports/mangueiral_top15/README.md`
- `reports/cruzeiro_top15/README.md`
- `reports/cruzeiro_vs_mangueiral/README.md`
Eles são snapshots datados, não preferências permanentes.
Top 3 Mangueiral em 22/07/2026:
1. anúncio 1279892 — QC 10 — R$ 595 mil;
2. anúncio 1319996 — QC 14 — R$ 649 mil;
3. anúncio 1377683 — QC 11 — R$ 585 mil, aderência inferida e pendente de avaliação do usuário.
A ordem incorpora avaliação direta do usuário sobre os 15 finalistas: prioridade para cozinha e gourmet funcionais, conservação e baixa necessidade de intervenção; piscina, jacuzzi, banheira e cubas de vidro são incompatibilidades ou fortes negativos. O teto eliminatório de R$ 750 mil retirou `1352633` (R$ 755 mil); o novo `1401074`, QC 13 e R$ 720 mil, entrou em 8º com aderência inferida e pendente de avaliação pessoal.
Após o feedback, o funil foi refeito sobre os 78 anúncios ativos. `1377683`, `1371718` e `1140317` entraram no Top 5 por aderência inferida nas fotos e descrições, mas ainda não possuem nota pessoal e não devem ser tratados como preferência confirmada.
Top 3 Cruzeiro em 21/07/2026:
1. anúncio 1345146 — SHCES 1207 A — R$ 670 mil;
2. anúncio 1357415 — SHCES 1109 — R$ 630 mil;
3. anúncio 1357633 — SHCES 309 — R$ 600 mil.
---
## 6. Pendências principais
- visitar o Mangueiral e validar QCs, trânsito, comércio, ruído e sensação de segurança;
- atualizar saldos e taxas antes de simulações finais;
- definir limite confortável de prestação, não apenas capacidade bancária;
- confirmar documentação, planta original e regularidade das ampliações;
- aumentar a série histórica antes de conclusões de liquidez e tendência;
- corrigir a instabilidade de `creci`/campos contaminados antes de usar o histórico bruto como mudança real.

537
docs/DATA_MODEL.md

@ -1,481 +1,254 @@
# Modelo de dados — referência conceitual # Modelo de dados conceitual
## 1. Aviso ## 1. Aviso
Este documento descreve o modelo lógico desejável para análise. Este documento descreve a interpretação desejada. O esquema real deve ser registrado separadamente em `docs/ACTUAL_SCHEMA.md`.
Ele **não afirma** que o banco SQLite atual possui essas tabelas ou colunas. O esquema real deve ser inspecionado antes de qualquer consulta. Não migrar o banco original apenas para fazê-lo coincidir com este modelo.
Não migre o banco original para este modelo sem autorização.
--- ---
## 2. Entidades fundamentais ## 2. Entidades essenciais
O projeto deve distinguir cinco conceitos:
1. **fonte**: site, portal, corretor ou canal; ### `scrape_run`
2. **anúncio**: publicação identificável em uma fonte;
3. **observação**: estado do anúncio em uma data de coleta;
4. **imóvel físico**: unidade residencial que pode aparecer em vários anúncios;
5. **execução de coleta**: processo técnico que obteve os dados.
Essa separação é essencial para séries históricas e deduplicação. Representa uma execução de coleta.
---
## 3. Fonte
Entidade conceitual: `source`
Campos desejáveis: Campos desejáveis:
- `source_id` - `run_id`;
- `name` - `started_at`;
- `base_url` - `finished_at`;
- `source_type` - `status`;
- `active` - `scraper_version`;
- `notes` - `configuration_hash`;
- `created_at` - páginas e erros;
- contagens de anúncios e fotos;
Não usar domínio ou nome de corretor como identificador único sem normalização. - resumo de status HTTP.
---
## 4. Execução de coleta
Entidade conceitual: `scrape_run`
Campos desejáveis:
- `run_id`
- `source_id`
- `started_at`
- `finished_at`
- `status`
- `pages_requested`
- `pages_succeeded`
- `pages_failed`
- `items_seen`
- `items_inserted`
- `items_updated`
- `http_status_summary`
- `scraper_version`
- `configuration_hash`
- `error_summary`
Serve para identificar:
- períodos sem coleta;
- mudanças de cobertura;
- falhas;
- alterações de código;
- diferenças entre ausência real e ausência causada por erro.
---
## 5. Anúncio
Entidade conceitual: `listing`
Representa a identidade do anúncio, não cada coleta.
Campos desejáveis:
- `listing_id`
- `source_id`
- `source_listing_id`
- `canonical_url`
- `first_seen_at`
- `last_seen_at`
- `last_successful_seen_at`
- `status`
- `seller_type`
- `seller_name_normalized`
- `broker_registration`
- `property_candidate_id`
- `created_at`
- `updated_at`
Chave natural potencial:
```text
(source_id, source_listing_id)
```
A URL não deve ser a única chave, pois pode mudar.
---
## 6. Observação histórica ### `listing`
Entidade conceitual: `listing_snapshot` Identidade do anúncio na fonte.
Representa o conteúdo observado em uma execução. - identificador interno;
- identificador do portal;
- URL canônica;
- primeira e última aparição;
- status;
- anunciante;
- vínculo com imóvel provável.
Campos desejáveis: ### `listing_observation`
- `snapshot_id` Estado do anúncio em uma data de coleta.
- `listing_id`
- `run_id`
- `observed_at`
- `title`
- `description`
- `price`
- `condominium_fee`
- `property_tax`
- `area_private`
- `area_useful`
- `area_built`
- `area_total`
- `bedrooms`
- `suites`
- `bathrooms`
- `parking_spaces`
- `floor`
- `elevator`
- `furnished`
- `property_type`
- `address_raw`
- `neighborhood_raw`
- `latitude`
- `longitude`
- `image_count`
- `status_raw`
- `payload_hash`
- `raw_payload_reference`
Não sobrescrever o histórico quando um anúncio muda.
Se os dados atuais usam uma única linha mutável por anúncio, documentar essa limitação.
--- - data;
- título e descrição;
- preço;
- condomínio e IPTU;
- áreas separadas;
- quartos, suítes, banheiros e vagas;
- endereço bruto;
- status bruto;
- hash do conteúdo;
- referência ao HTML.
## 7. Imóvel físico candidato Não sobrescrever histórico.
Entidade conceitual: `property_candidate` ### `photo`
Representa uma hipótese de que anúncios diferentes descrevem o mesmo imóvel. - anúncio ou observação;
- URL;
- caminho local;
- posição;
- hash criptográfico;
- hash perceptual;
- dimensões;
- status de download.
Campos desejáveis: A posição da primeira foto deve ser preservada, pois ela é usada na auditoria visual de disponibilidade.
- `property_candidate_id` ### `property_candidate`
- `canonical_region`
- `canonical_address`
- `canonical_unit`
- `property_type`
- `original_floor_plan`
- `dedup_confidence`
- `dedup_method`
- `review_status`
- `created_at`
- `updated_at`
Não tratar correspondência probabilística como certeza. Hipótese de imóvel físico comum a um ou mais anúncios.
Estados sugeridos: - região e endereço normalizados;
- tipologia;
- planta original provável;
- método de deduplicação;
- confiança;
- estado de revisão.
- `unreviewed` Não converter correspondência probabilística em certeza.
- `probable`
- `confirmed`
- `rejected`
- `ambiguous`
--- ---
## 8. Endereço e localização ## 3. Localização no DF
Entidade conceitual: `location` A normalização deve suportar:
Campos desejáveis:
- `location_id`
- `region`
- `administrative_region`
- `neighborhood`
- `superquadra`
- `quadra`
- `block`
- `qc`
- `street`
- `lot`
- `unit`
- `postal_code`
- `latitude`
- `longitude`
- `geocode_precision`
- `geocode_source`
No Distrito Federal, o endereço deve permitir estruturas como:
- região administrativa;
- bairro;
- SHCES; - SHCES;
- quadra; - quadra;
- bloco; - bloco;
- superquadra;
- conjunto; - conjunto;
- lote;
- QC; - QC;
- rua;
- lote;
- unidade;
- condomínio; - condomínio;
- unidade. - latitude e longitude;
- precisão da geocodificação.
Não forçar todos os endereços a um modelo de rua e número. Não forçar todos os endereços ao modelo rua/número.
--- ---
## 9. Mídia ## 4. Áreas
Entidade conceitual: `listing_media`
Campos desejáveis:
- `media_id`
- `snapshot_id`
- `media_url`
- `media_type`
- `position`
- `content_hash`
- `perceptual_hash`
- `width`
- `height`
- `download_status`
Hashes perceptuais de imagens podem ajudar na deduplicação, mas não devem decidir sozinhos.
---
## 10. Histórico de preço
O histórico pode ser derivado de `listing_snapshot`.
View conceitual:
```sql
SELECT
listing_id,
observed_at,
price,
price - LAG(price) OVER (
PARTITION BY listing_id
ORDER BY observed_at
) AS absolute_change
FROM listing_snapshot;
```
Regras:
- ignorar repetições idênticas; Manter separadas:
- registrar aumentos e reduções;
- não interpretar ausência como venda;
- identificar alterações simultâneas de área ou tipologia;
- detectar preços promocionais artificiais;
- separar moeda e unidades.
---
## 11. Deduplicação
### 11.1 Sinais fortes
- mesmo identificador na fonte;
- mesmo endereço e unidade;
- mesmo conjunto de imagens;
- mesmo telefone ou corretor com descrição muito semelhante;
- mesma planta, metragem e características;
- coordenadas muito próximas;
- texto quase idêntico.
### 11.2 Sinais moderados
- mesma quadra e bloco;
- mesmo preço;
- mesma área;
- mesmos quartos e vagas;
- datas próximas;
- imagens parcialmente coincidentes.
### 11.3 Sinais fracos
- mesmo bairro;
- título genérico;
- preço arredondado;
- número de quartos isolado.
### 11.4 Regras
- manter o anúncio original;
- criar relacionamento de duplicidade;
- registrar método e confiança;
- permitir revisão manual;
- não fundir registros de forma irreversível;
- não usar apenas texto normalizado;
- evitar que atualizações do mesmo anúncio sejam contadas como imóveis novos.
---
## 12. Normalização
### Preço
- armazenar como número inteiro em centavos ou valor monetário consistente;
- preservar valor bruto;
- registrar moeda;
- detectar preço por mês confundido com preço total;
- detectar valores incompletos.
### Área
Manter campos separados:
- privativa; - privativa;
- útil; - útil;
- construída; - construída;
- total; - total;
- terreno. - terreno;
- ampliada declarada.
Preservar valor bruto e fonte.
No Mangueiral, áreas anunciadas frequentemente misturam área original e ampliação. Não calcular preço por m² sem validação.
Nunca preencher uma área com outra apenas para completar dados. ---
### Quartos ## 5. Quartos e planta
Separar: Separar:
- quartos; - quartos anunciados;
- quartos utilizáveis;
- suítes; - suítes;
- dependência; - dependência;
- escritório; - escritório;
- quarto reversível; - quarto adaptado;
- tipologia original; - planta original provável;
- tipologia anunciada. - confiança da classificação.
### Localização No Mangueiral, a planta original de três quartos é um filtro obrigatório.
Preservar: ---
- texto bruto; ## 6. Status e disponibilidade
- versão normalizada;
- componentes extraídos;
- confiança da extração.
### Datas Campos derivados desejáveis:
Usar ISO 8601 e timezone explícito quando relevante. - ativo na última coleta;
- inativo;
- vendido na primeira foto;
- vendido na descrição;
- republicado;
- duplicata provável;
- conflito de dados;
- data da última evidência.
--- Ausência não é prova de venda.
## 13. Qualidade dos dados ---
Criar métricas por fonte, período e região:
- percentual sem preço; ## 7. Deduplicação
- percentual sem área;
- percentual sem localização;
- percentual com preço por m² calculável;
- duplicidade estimada;
- taxa de mudança de identificador;
- cobertura diária;
- falhas de coleta;
- distribuições implausíveis;
- inconsistência entre título e campos;
- divergência entre snapshots.
--- ### Sinais fortes
## 14. Inspeção inicial do SQLite - mesmo ID da fonte;
- endereço e unidade;
- conjunto de imagens;
- implantação idêntica;
- telefone e descrição coincidentes.
Consultas somente leitura úteis: ### Sinais moderados
### Tabelas e views - quadra/bloco/QC;
- área, preço e quartos;
- texto semelhante;
- datas próximas;
- fotos parcialmente coincidentes.
```sql ### Regras
SELECT
type,
name,
tbl_name,
sql
FROM sqlite_master
ORDER BY type, name;
```
### Contagem aproximada de tabelas - preservar anúncios originais;
- criar grupo de imóvel provável;
- registrar confiança e método;
- permitir revisão manual;
- não fundir irreversivelmente.
Gerar consultas após listar os nomes. Não concatenar nomes externos sem validação. ---
### Colunas ## 8. Histórico de preço
```sql Derivar por anúncio e por imóvel provável.
PRAGMA table_info('nome_da_tabela');
```
### Chaves estrangeiras Registrar:
```sql - primeiro preço;
PRAGMA foreign_key_list('nome_da_tabela'); - preço atual;
``` - reduções e aumentos;
- republicações;
- mudanças simultâneas de área, quartos ou descrição;
- períodos de ausência.
### Índices Não interpretar retirada como venda.
```sql ---
PRAGMA index_list('nome_da_tabela');
```
### Integridade ## 9. Qualidade dos dados
```sql Métricas mínimas:
PRAGMA quick_check;
```
`quick_check` é somente leitura, mas pode ser custoso em bancos grandes. Executar quando apropriado. - nulos por campo;
- preços implausíveis;
- áreas implausíveis;
- divergência entre título e campos;
- cobertura por dia;
- falhas por página;
- fotos ausentes;
- duplicidade estimada;
- anúncios com selo de vendido;
- mudanças de ID;
- distribuição por região e tipologia.
--- ---
## 15. Camada derivada recomendada ## 10. Camada derivada
Sem alterar o banco original, criar: Sugestão:
```text ```text
data/derived/ data/derived/
├── listings_clean.parquet ├── listings_clean.parquet
├── snapshots_clean.parquet ├── observations_clean.parquet
├── photos_index.parquet
├── property_candidates.parquet ├── property_candidates.parquet
├── price_history.parquet ├── price_history.parquet
├── locations_normalized.parquet ├── locations_normalized.parquet
└── quality_metrics.parquet └── quality_metrics.parquet
``` ```
Ou um SQLite derivado: Registrar versão do código, data, banco de origem e contagens de entrada/saída.
```text
data/derived/analytics.sqlite
```
Toda derivação deve registrar:
- data;
- versão do código;
- fonte;
- filtros;
- hash ou identificação do banco de origem;
- contagem de entrada e saída.
--- ---
## 16. Dicionário do esquema real ## 11. Implementação aditiva v2 — 22/07/2026
Após inspecionar o banco, produzir:
```text
docs/ACTUAL_SCHEMA.md
```
Esse arquivo deve conter: Com autorização explícita do usuário e backup consistente, parte deste modelo foi incorporada ao banco operacional sem remover dados:
- diagrama das tabelas; - campos brutos preservados e colunas normalizadas em `listings`;
- descrição de cada coluna; - `stable_content_hash` separado do hash bruto legado;
- chaves; - `is_primary_gallery`, origem e revisão em `photos`;
- índices; - `availability_observations` para evidência de disponibilidade;
- cardinalidades; - `data_quality_issues` para quarentenas reversíveis;
- exemplos pequenos; - `listing_change_events` para mudanças materiais futuras;
- problemas conhecidos; - escopo e configuração das futuras execuções em `crawl_runs`;
- correspondência com este modelo conceitual; - visões `v_primary_photos`, `v_open_data_quality_issues` e `v_listing_current_availability`.
- decisões de interpretação.
Não alterar `DATA_MODEL.md` para simplesmente espelhar um esquema ruim. Manter separação entre modelo conceitual e implementação real. As tabelas e colunas antigas continuam preservadas para reprodução histórica.

195
docs/FINANCIAL_STRATEGY.md

@ -0,0 +1,195 @@
# Estratégia financeira
## 1. Snapshot pessoal
**Data de referência:** julho de 2026
| Item | Valor de trabalho |
|---|---:|
| Renda bruta mensal | aproximadamente R$ 18.500 |
| Recursos líquidos | R$ 222.000 |
| FGTS | R$ 130.000 |
| Reserva líquida mínima pós-compra | R$ 50.000 |
| Poupança adicional | R$ 15.000 a cada seis meses |
| Aluguel-base usado nas comparações | R$ 3.200/mês |
| Faixa de imóveis-alvo | R$ 650.000–R$ 750.000 |
| Preço máximo tolerado do imóvel | **R$ 750.000** |
O aluguel-base exclui condomínio e IPTU. Não misturar com valores brutos ou contratuais sem decomposição.
Esses valores são premissas datadas, não constantes permanentes.
O preço de R$ 750.000 é o teto máximo tolerado para o imóvel, não o orçamento recomendado. Custos de aquisição, mudança, reforma e regularização ficam fora desse preço e continuam sujeitos à preservação da reserva líquida mínima.
---
## 2. Restrições
- preservar ao menos R$ 50.000 líquidos depois de entrada, custos, mudança e adequações;
- FGTS não é reserva de emergência;
- não usar toda a capacidade bancária como orçamento;
- não aceitar prestação inicial desconfortável apenas porque cabe na análise de crédito;
- incorporar custos de transação e reforma;
- evitar imóvel que exija comprometer a reserva logo após a compra.
- não recomendar nem simular como candidato aceitável um imóvel com preço acima de R$ 750.000, salvo se o usuário alterar explicitamente esse teto.
---
## 3. Programas e linhas excluídos
### Pró-Cotista
Fora do conjunto atual de opções.
Regras consideradas pelo usuário:
- renda bruta máxima de R$ 12.000;
- imóvel usado de até R$ 500.000.
A renda aproximada de R$ 18.500 e a faixa-alvo de R$ 650.000–R$ 750.000 não se enquadram.
### Minha Casa Minha Vida — Faixa 4
Não incluir nas simulações atuais.
### Imóvel irregular
Financiamento bancário tradicional depende de matrícula, registro e regularidade suficientes para a garantia. Não presumir financiamento de imóvel sem escritura/matrícula regular.
---
## 4. Financiamento tradicional
É a referência principal para compra quando o imóvel correto aparecer.
Simulações recentes citaram, aproximadamente:
- Caixa: 11,5% ao ano;
- Banco do Brasil: 11,7% ao ano.
Essas taxas são snapshots de propostas/simulações, não taxas vigentes garantidas. Sempre substituir por CET atual e documentos do banco.
### Campos obrigatórios em qualquer simulação
- preço do imóvel;
- entrada em dinheiro;
- FGTS;
- reserva preservada;
- ITBI, registro e tarifas;
- reforma e mudança;
- valor financiado;
- SAC ou Price;
- taxa nominal, efetiva e CET;
- indexador;
- prazo;
- seguros;
- primeira e última prestação;
- amortização extraordinária;
- uso de FGTS a cada 24 meses;
- horizonte de permanência;
- custo de oportunidade.
### Comparação com o aluguel
O usuário observou que, para um imóvel em torno de R$ 700.000, a prestação inicial estimada poderia superar o aluguel atual.
A análise deve comparar:
- fluxo mensal;
- patrimônio acumulado;
- liquidez;
- risco;
- estabilidade;
- custos de manutenção;
- reajuste do aluguel;
- valorização do imóvel;
- retorno líquido alternativo;
- custos de venda.
Não decidir apenas porque a prestação é maior ou menor que o aluguel.
---
## 5. Consórcio BB analisado
Cenário específico discutido, não contratado:
- carta: R$ 660.000;
- prazo informado: 161 meses;
- taxa de administração: 0,11% ao mês, aproximadamente 18,7% no total da carta;
- lance pretendido: 60%, equivalente a R$ 420.000;
- recursos próprios considerados: R$ 300.000;
- empréstimo complementar: R$ 120.000;
- empréstimo: 120 parcelas de R$ 1.596,66;
- parcela pós-lance estimada: aproximadamente R$ 1.924;
- último lance vencedor informado: cerca de 58%;
- lances recentes mencionados: aproximadamente 58,99% a 60%;
- grupo não era novo.
Relatos do gerente sobre sazonalidade de agosto/setembro, competição em outubro e participação de funcionários do BB são evidência anedótica e não devem ser modelados como regra sem histórico do próprio grupo.
### Conclusão de trabalho
- o consórcio pode ter vantagem de custo nominal em algumas combinações;
- exige imobilização de recursos e aceita risco de contemplação;
- pode demandar empréstimo complementar, reduzindo a vantagem;
- financiamento tradicional oferece maior certeza, flexibilidade e execução imediata;
- a proposta específica foi deixada passar;
- a estratégia atual é manter paciência e precisão.
---
## 6. Custos de compra
Incluir, conforme o caso:
- ITBI;
- registro;
- escritura quando aplicável;
- avaliação bancária;
- tarifas;
- seguros;
- mudança;
- reforma;
- mobiliário;
- regularização;
- condomínio e IPTU proporcionais;
- contingência.
Não usar percentual genérico sem conferir regras e valores atuais.
---
## 7. Cenários obrigatórios
Toda recomendação financeira deve apresentar ao menos:
- conservador;
- central;
- otimista;
- cenário de estresse.
Estressar:
- renda menor;
- juros ou CET maiores;
- reforma acima do orçamento;
- valorização baixa;
- retorno de investimento menor;
- manutenção extraordinária;
- redução da poupança;
- necessidade de vender antes do horizonte.
---
## 8. Critério de decisão
A compra é financeiramente aceitável somente quando:
- a reserva é preservada;
- o custo mensal é confortável, não apenas aprovado;
- o imóvel atende à planta e à localização;
- custos de adequação são conhecidos;
- o horizonte justifica custos de transação;
- a comparação com aluguel não depende de premissas otimistas;
- a decisão continua boa sem contar com valorização excepcional.

138
docs/LOCATION_CRITERIA.md

@ -0,0 +1,138 @@
# Critérios de localização
## 1. Regra geral
Avaliar localização em três níveis:
1. região;
2. quadra, bloco, QC ou condomínio;
3. posição exata da unidade.
A classificação geral nunca substitui a posição concreta.
---
## 2. Jardins Mangueiral — prioridade principal
### 2.1 Hierarquia de QCs
| Nível | QCs | Uso |
|---|---|---|
| Preferenciais | 8 e 9 | Priorizar triagem e visitas quando o imóvel também for bom |
| Alternativas fortes | 6 e 7 | Manter entre as candidatas principais |
| Também boas | 10 e 11 | Avaliar normalmente, sem penalidade automática |
| Não classificadas | demais QCs | Analisar caso a caso; não excluir somente pela QC |
A hierarquia veio de relatos de moradores e deve ser marcada como **anedótica**.
O ranking de imóveis concretos pode produzir candidatos fortes em outras QCs. No snapshot de 21/07/2026, por exemplo, imóveis em QC 10, QC 6 e QC 8 lideraram, enquanto unidades em QCs 14, 12, 4, 5 e 3 também entraram no Top 15.
### 2.2 O que “bem localizada” significa
- acesso prático a mercado, farmácia e serviços;
- qualidade do trajeto a pé;
- facilidade de entrada e saída;
- tempo real em horários relevantes;
- circulação interna;
- topografia;
- estacionamento de visitantes;
- iluminação noturna;
- distância de portaria e áreas técnicas;
- distância de quadra esportiva, salão, parque infantil e lixeiras;
- privacidade;
- posição solar;
- ruído.
### 2.3 Posição da casa
Uma casa na QC 8 ou 9 pode perder prioridade se estiver:
- junto a área social barulhenta;
- exposta a via interna movimentada;
- com pouca privacidade;
- em posição quente e mal ventilada;
- próxima a equipamentos ou lixeiras;
- em trecho ruim para estacionamento.
Uma unidade excepcional nas QCs 6, 7, 10, 11 ou mesmo em QC não classificada pode superar uma opção mediana nas QCs preferenciais.
### 2.4 Trânsito e rotina
O custo do deslocamento ao Plano Piloto deve ser medido em campo. A flexibilidade de horário, home office e o horário escolar no início da tarde reduzem parte do impacto, mas não o eliminam.
Registrar tempos em:
- manhã de dia útil;
- fim de tarde;
- noite;
- fim de semana;
- trajeto ao SBS;
- trajeto ao Sudoeste;
- trajeto a São Sebastião.
---
## 3. Cruzeiro — alternativa secundária
### 3.1 Ranking interno de quadras
Esse ranking orienta a localização relativa de apartamentos concretos no Cruzeiro Novo.
| Faixa | Quadras SHCES |
|---|---|
| Ouro | 1501, 1503, 1505, 1203, 1205, 1207 |
| Prata | 1103, 1105, 1107, 1109, 1111, 1303, 1305, 1307, 1309 |
| Bronze | conjunto 1400 |
| Caso a caso | 1211 e 1507 |
Regras:
- a faixa não compensa um apartamento ruim;
- uma unidade Prata bem reformada, silenciosa e acessível pode superar uma Ouro problemática;
- edifício, bloco, fachada, andar e posição são decisivos;
- o ranking não torna o Cruzeiro prioritário sobre o Mangueiral.
### 3.2 Imóveis concretos podem fugir da hierarquia
O Top 15 de 21/07/2026 incluiu imóveis em SHCES 1207, 1109, 1303 e também em 309, 911, 703, 207, 305, 301, 101, 609 e 203.
Essas entradas não redefinem automaticamente as faixas. Elas mostram que qualidade, preço, estado e funcionalidade podem justificar investigação fora da hierarquia original.
### 3.3 Critérios locais
Avaliar:
- acesso ao Eixo Monumental e Plano Piloto;
- comércio útil à rotina;
- caminhabilidade;
- ônibus e vias;
- tráfego e ruído;
- estacionamento e garagem;
- iluminação e arborização;
- estado dos blocos vizinhos;
- proximidade de bares, escolas, templos e quadras esportivas;
- posição do bloco em relação à rua;
- orientação solar e ventilação;
- vista e privacidade.
### 3.4 Observação de campo
A visita anterior ao Cruzeiro reduziu sua prioridade porque o perfil de comércio percebido não correspondeu ao desejado. Tratar isso como preferência pessoal e hipótese a validar, não como julgamento socioeconômico da região.
---
## 4. Protocolo de visita de localização
Para um candidato real:
- visitar em dia útil;
- observar pico de trânsito;
- visitar à noite ou no fim de semana;
- testar estacionamento;
- caminhar até serviços;
- medir ruído com janelas abertas e fechadas;
- conversar com moradores quando possível;
- verificar iluminação, circulação e segurança percebida;
- registrar tempos reais de deslocamento.
Não depender apenas de mapa, anúncio ou corretor.

308
docs/PROJECT_CONTEXT.md

@ -2,264 +2,152 @@
## 1. Objetivo ## 1. Objetivo
Construir uma base histórica e uma metodologia confiável para apoiar a compra de um imóvel residencial no Distrito Federal. Construir uma base histórica de anúncios e uma metodologia de avaliação para uma compra residencial de longo prazo no Distrito Federal.
O objetivo não é apenas encontrar o menor preço. A decisão deve equilibrar: A compra deve oferecer:
- segurança patrimonial de longo prazo; - residência funcional e estável;
- adequação funcional do imóvel; - segurança patrimonial;
- qualidade de vida; - custo mensal sustentável;
- liquidez; - preservação de reserva de emergência;
- custo total de aquisição e manutenção; - baixo risco de reforma e custos escondidos;
- risco de reformas; - adequação à rotina familiar;
- impacto financeiro mensal; - liquidez razoável;
- fatores psicológicos associados a possuir uma residência estável. - possibilidade de permanência por 10 a 20 anos.
A análise deve ser lenta, comparativa e baseada em evidências. Não existe urgência artificial para comprar. O projeto não busca comprar rapidamente. Busca reduzir a probabilidade de arrependimento.
--- ---
## 2. Filosofia de decisão ## 2. Evolução da prioridade regional
### Princípios confirmados ### Fase anterior
- Evitar comprar apenas por medo de valorização futura. Cruzeiro chegou a ser tratado como candidato principal por centralidade, acesso ao Plano Piloto e tradição patrimonial.
- Evitar comprometer a reserva de emergência.
- Não aceitar grandes concessões de planta, conservação ou funcionalidade apenas para alcançar um preço nominal.
- Considerar reforma como custo financeiro, operacional e psicológico.
- Preferir imóvel pronto ou com intervenções pequenas e previsíveis.
- Comparar comprar com continuar alugando.
- Considerar que a compra será provavelmente em uma região diferente da moradia atual.
- Separar segurança emocional de justificativas financeiras.
- Reconhecer o valor psicológico de possuir um imóvel físico de longo prazo, sem transformar isso em justificativa para uma compra ruim.
- Tratar preço anunciado como ponto inicial de negociação, não como valor de mercado definitivo.
--- ### Entendimento atual
## 3. Situação residencial atual
Últimos valores consolidados no histórico do projeto: Jardins Mangueiral tornou-se a prioridade principal. Cruzeiro permanece como alternativa secundária e oportunística.
- localização atual: Asa Sul, Brasília; Motivos para a mudança:
- imóvel alugado: aproximadamente 66 m²;
- aluguel: R$ 3.200 por mês;
- condomínio e IPTU não incluídos nesse valor;
- reajuste anual usado nas simulações anteriores: 4,5%.
Esses números devem ser confirmados antes de novas simulações, pois podem mudar. - casas mais novas e maior oferta no Mangueiral;
- maior chance de encontrar planta e conservação adequadas;
- percepção de ambiente residencial, segurança e silêncio;
- serviços de perfil familiar em crescimento;
- melhor integração com São Sebastião;
- flexibilidade de horário e trabalho remoto reduz parte do custo do trânsito;
- no Cruzeiro, imóveis concretos apresentaram mais concessões de reforma, garagem, elevador e funcionalidade.
A comparação aluguel versus compra deve considerar que o padrão, a localização e a tipologia dos imóveis candidatos podem ser diferentes do imóvel alugado atual. Em visita ao Cruzeiro, o usuário percebeu uma composição de comércio e serviços menos alinhada às suas preferências cotidianas, com menor presença percebida de padarias, farmácias, restaurantes e academias. Essa é uma percepção pessoal de campo; não deve ser convertida em inferência de renda, segurança ou qualidade objetiva da região.
--- ---
## 4. Regiões prioritárias ## 3. Rotina e logística
### 4.1 Cruzeiro
O Cruzeiro, especialmente o Cruzeiro Novo, é um dos dois principais candidatos.
Aspectos relevantes:
- localização central;
- acesso ao Plano Piloto;
- blocos e quadras com diferenças importantes;
- estoque antigo, com variação de conservação;
- risco de imóveis que exigem reforma;
- diferenças de estacionamento, garagem, elevador, ruído e posição do bloco;
- apartamentos de dois e três quartos podem ter áreas totais semelhantes;
- preço deve ser analisado em conjunto com planta, estado e custos de adequação.
### 4.2 Jardins Mangueiral Contexto relevante:
Jardins Mangueiral é o outro principal candidato. - trabalho associado à área central de Brasília/SBS;
- filha estuda no Sudoeste e entra no início da tarde;
- existe flexibilidade de horário e trabalho remoto em parte da rotina;
- namorada e familiares dela vivem em São Sebastião;
- Jardins Mangueiral pode melhorar a logística da relação e da convivência familiar.
Aspectos relevantes: A compra deve continuar racional e funcional mesmo se as circunstâncias afetivas mudarem. A proximidade da namorada é benefício, não fundamento exclusivo da decisão.
- casas mais novas em comparação com parte do estoque do Cruzeiro;
- sensação de segurança, silêncio e residência de longo prazo;
- proximidade de São Sebastião, onde vivem a namorada do usuário e familiares dela;
- potencial de desenvolvimento da região em horizonte de 15 a 20 anos;
- impacto de trânsito parcialmente mitigado por flexibilidade de horário e trabalho remoto;
- logística compatível com o horário escolar da filha em parte dos dias;
- necessidade de avaliar ruído de vizinhança e qualidade das adaptações internas.
Somente casas de planta original de três quartos fazem parte do conjunto de candidatos.
--- ---
## 5. Preferências funcionais ## 4. Preferências habitacionais
O imóvel ideal deve:
- funcionar como residência de longo prazo; - imóvel pronto ou com intervenções pequenas e previsíveis;
- exigir pouca ou nenhuma reforma relevante; - evitar grandes concessões de planta, conservação e funcionalidade;
- ter planta coerente; - três quartos funcionais são valorizados;
- evitar circulação desperdiçada; - boa iluminação e ventilação;
- ter boa iluminação e ventilação; - espaço social útil;
- oferecer espaço social útil; - pouca circulação desperdiçada;
- evitar duplicações de cozinha ou área gourmet sem função clara; - armazenamento adequado;
- ter conservação compatível com o preço; - ruído controlado;
- minimizar custos escondidos; - manutenção sustentável;
- preservar flexibilidade para mudanças futuras da família. - documentação financiável;
- flexibilidade para mudanças futuras da família.
No Jardins Mangueiral, o fechamento do quintal não é automaticamente negativo. A análise deve avaliar se a área foi convertida em espaço realmente útil, ventilado e integrado. Preferências funcionais confirmadas em avaliação direta dos anúncios:
--- - cozinha bem planejada e, idealmente, semi-integrada ao convívio externo;
- churrasqueira e gourmet conectados ao fluxo social da casa;
## 6. Referência financeira - prioridade para conservação e baixa necessidade de intervenção antes da mudança;
- piscina, jacuzzi e banheira não são desejadas;
### Último retrato consolidado - cubas de vidro, marcenaria ruim e áreas externas abandonadas são fortes negativos;
- fechamento em vidro deve ser penalizado quando houver risco de calor excessivo;
Valores informados no histórico, sujeitos a atualização: - closet criado pela perda de um quarto pode exigir reversão futura;
- placas solares são bônus, não compensação para planta ou funcionalidade inadequada.
- recursos líquidos: R$ 222.000;
- saldo de FGTS: R$ 130.000;
- reserva mínima desejada após a compra: R$ 50.000;
- capacidade de poupança: R$ 15.000 a cada seis meses;
- faixa de preço mais compatível com os imóveis desejados: aproximadamente R$ 650.000 a R$ 750.000;
- faixa anterior de referência: R$ 600.000.
Não use esses valores como atuais sem registrar a data da análise e pedir confirmação quando a conclusão depender deles.
### Regras financeiras
- FGTS não deve ser tratado como liquidez disponível para emergências.
- Custos de compra devem ser adicionados ao preço:
- ITBI;
- registro;
- escritura quando aplicável;
- avaliação;
- tarifas;
- mudança;
- reparos;
- mobiliário;
- eventual reforma;
- custos de oportunidade.
- A reserva mínima de R$ 50.000 não deve ser consumida pela entrada, custos ou reforma.
- A prestação não deve ser analisada isoladamente.
- Comparar:
- aluguel;
- condomínio;
- IPTU;
- seguros;
- manutenção;
- juros;
- amortização;
- custo de oportunidade da entrada;
- valorização;
- reajuste do aluguel;
- liquidez do imóvel.
- Distinguir patrimônio acumulado de fluxo de caixa mensal.
--- ### Quintal e ampliação no Mangueiral
## 7. Financiamento e consórcio A perda do quintal original não é defeito automático. É aceitável quando a área vira ambiente útil de convivência, refeição, trabalho ou armazenamento.
### Financiamento É negativa quando:
Simulações anteriores consideraram diferentes cenários de: - duplica a cozinha sem necessidade;
- reduz ventilação e iluminação;
- cria calor ou umidade;
- compromete drenagem e impermeabilização;
- produz circulação inútil;
- ocupa a área externa sem ganho funcional real.
- SAC; ---
- prazos de 20, 30 e até 35 anos;
- juros próximos de 9% ao ano em alguns cenários;
- amortizações periódicas com FGTS;
- amortizações extraordinárias em dinheiro.
Toda nova simulação deve explicitar:
- taxa nominal e efetiva;
- indexador;
- CET;
- sistema de amortização;
- prazo;
- entrada;
- custos acessórios;
- uso de FGTS;
- estratégia de amortização;
- inflação;
- retorno líquido alternativo;
- horizonte de permanência.
### Pró-cotista
A estratégia não deve assumir que o Pró-cotista estará disponível. O usuário já percebeu que provavelmente não receberá essa linha.
### Consórcio
O consórcio do Banco do Brasil foi considerado como alternativa.
Informações recebidas do gerente, tratadas como relatos e não como garantias:
- grupos com desconto podem atrair participantes dispostos a oferecer lances elevados; ## 5. Filosofia financeira e psicológica
- agosto e setembro podem ser meses relativamente melhores para ofertar lance;
- outubro pode ficar mais competitivo por coincidir com pagamento de PLR de funcionários do BB;
- muitos participantes dos consórcios do BB seriam funcionários do próprio banco.
Essas afirmações precisam ser verificadas com dados do grupo sempre que forem usadas. Não extrapolar um grupo para todos os grupos. O usuário valoriza possuir uma residência física de longo prazo. Essa segurança tem peso legítimo na decisão, especialmente com o avanço da idade e a preocupação com estabilidade futura.
A estratégia atual é não agir com pressa e observar os percentuais de lances ao longo do tempo. Ao mesmo tempo:
--- - reserva de emergência não deve ser sacrificada;
- prestação maior que o aluguel atual exige justificativa e folga;
- capacidade máxima de financiamento não é orçamento confortável;
- custo de oportunidade da entrada deve ser medido;
- reforma e mudança têm custo psicológico, além do financeiro;
- comprar para aliviar ansiedade pode produzir uma decisão pior.
## 8. Fatores familiares e logísticos A conclusão financeira não deve ignorar o benefício de estabilidade, e o benefício psicológico não deve ignorar juros, liquidez e risco.
- O usuário tem uma filha em idade escolar.
- A namorada mora em São Sebastião e tem um filho.
- Jardins Mangueiral pode reduzir a distância para a namorada e a família dela.
- O usuário tem flexibilidade de trabalho e de horário em parte da semana.
- A logística familiar deve ser considerada, mas não deve justificar um imóvel inadequado.
- A compra deve continuar válida mesmo se circunstâncias relacionais mudarem.
--- ---
## 9. Horizonte ## 6. Mercado e dados
A decisão deve ser avaliada como residência e patrimônio de longo prazo. O banco contém anúncios, observações históricas e fotos. Fotografias são essenciais porque campos estruturados e descrições não capturam adequadamente:
Questões centrais: - planta;
- conservação;
- iluminação;
- ventilação;
- qualidade da reforma;
- fechamento do quintal;
- redundância de áreas gourmet;
- sinais visuais de anúncio vendido;
- duplicatas entre corretores.
- O imóvel continuará funcional em 10, 15 ou 20 anos? Preço anunciado é evidência de oferta, não transação.
- A localização atende a rotina sem depender de uma única circunstância atual?
- O custo mensal permanece confortável em cenários adversos?
- Existe reserva para manutenção, saúde, carro e emergências?
- A planta permite adaptação?
- A revenda é razoável?
- A compra reduz ou aumenta a fragilidade financeira?
--- ---
## 10. Questões em aberto ## 7. Regiões fora do foco
Manter estas questões explícitas até que os dados permitam respondê-las: Outras regiões já foram discutidas, incluindo Asa Sul, Asa Norte, Guará I, Lúcio Costa, Águas Claras e outras. Elas não compõem o foco operacional atual.
- valor atual disponível em dinheiro e FGTS; Não ampliar automaticamente o universo de busca sem instrução. Elas podem ser usadas como referência comparativa quando isso responder a uma pergunta específica.
- limite confortável de prestação;
- prazo máximo desejável;
- custo total aceitável de aquisição;
- peso relativo entre Cruzeiro e Jardins Mangueiral;
- blocos e quadras prioritários no Cruzeiro;
- QCs prioritárias no Mangueiral após visitas;
- tolerância a imóveis sem elevador ou garagem;
- impacto real do trânsito;
- custo de manutenção de casas versus apartamentos;
- tamanho mínimo funcional;
- horizonte provável de permanência;
- estratégia final entre financiamento, consórcio e espera.
--- ---
## 11. Fonte de verdade ## 8. Horizonte de decisão
Este documento consolida preferências e premissas, mas não substitui uma confirmação recente quando a decisão depender de:
- saldo financeiro; Perguntas obrigatórias para qualquer candidato:
- renda;
- taxas bancárias;
- regras de FGTS;
- custos cartorários;
- disponibilidade de crédito;
- preços atuais;
- condições de consórcio;
- situação familiar ou logística.
Toda informação temporal deve carregar data de referência. - continuará funcional em 10, 15 ou 20 anos?
- atende à rotina sem depender de uma única circunstância atual?
- permite preservar reserva e capacidade de poupança?
- é habitável sem obra extensa?
- possui saída razoável para revenda?
- reduz ou aumenta a fragilidade financeira?
- oferece qualidade de vida suficiente para justificar a mudança?

536
docs/PROPERTY_CRITERIA.md

@ -1,438 +1,278 @@
# Critérios de avaliação dos imóveis # Critérios de avaliação dos imóveis
## 1. Estrutura da avaliação ## 1. Classificação
Classificar cada critério como: Cada achado deve ser marcado como:
- **eliminatório**: imóvel deve ser excluído; - **eliminatório**;
- **forte**: pode inviabilizar a compra mesmo com bom preço; - **forte**;
- **moderado**: influencia comparação e negociação; - **moderado**;
- **informativo**: deve ser registrado, mas não decide sozinho. - **informativo**;
- **pendente de confirmação**.
Não criar pesos numéricos arbitrários sem aprovação. Quando usar pontuação, mostrar os pesos, a justificativa e o efeito de cada critério. Falta de informação não é aprovação nem eliminação automática.
--- ---
## 2. Critérios eliminatórios gerais ## 2. Critérios eliminatórios ou de quarentena
Excluir ou colocar em quarentena analítica quando houver: - planta incompatível com a necessidade de longo prazo;
- no Mangueiral, planta originalmente de dois quartos;
- documentação que impeça a estratégia de financiamento e não possa ser regularizada;
- problema estrutural, umidade grave ou reforma extensa sem desconto suficiente;
- ruído intolerável;
- preço total incompatível com a reserva;
- localização materialmente divergente;
- anúncio com preço ou área absurdamente inconsistente;
- indicação visual ou textual de “VENDIDO” ou “negócio fechado”;
- duplicata provável sem identificação do anúncio principal;
- conflito relevante entre título, descrição, fotos e campos.
- documentação incompatível ou não esclarecida; Anúncio vendido deve ficar em quarentena histórica, não ser apagado.
- área, tipologia ou localização materialmente divergentes;
- planta que não atenda às necessidades de longo prazo;
- preço total incompatível com a preservação da reserva;
- problemas graves de umidade, infiltração ou estrutura;
- ruído intolerável identificado em visita;
- custo oculto que destrua a vantagem aparente;
- anúncio sem informação mínima para comparação;
- indícios relevantes de fraude ou inconsistência.
Um imóvel não deve ser eliminado apenas por falta de dados no anúncio. Nesse caso, classifique-o como pendente de verificação.
--- ---
## 3. Critérios gerais de qualidade ## 3. Critérios gerais
### 3.1 Planta e funcionalidade
Avaliar: ### Planta e funcionalidade
- distribuição dos cômodos; - três quartos utilizáveis;
- privacidade dos quartos; - circulação eficiente;
- largura de circulação; - privacidade;
- áreas mortas;
- integração social;
- capacidade de mobiliamento; - capacidade de mobiliamento;
- armazenamento; - armazenamento;
- número e localização de banheiros; - integração social;
- ventilação cruzada;
- iluminação natural; - iluminação natural;
- possibilidade de adaptação futura; - ventilação cruzada;
- conflito entre portas, móveis e circulação; - banheiros funcionais;
- uso real de varandas, quintais e áreas gourmet. - possibilidade de adaptação futura.
### 3.2 Estado de conservação ### Conservação e sistemas
Registrar separadamente: Avaliar separadamente:
- estrutura; - estrutura;
- elétrica; - elétrica;
- hidráulica; - hidráulica;
- revestimentos;
- esquadrias;
- telhado;
- impermeabilização; - impermeabilização;
- telhado;
- esquadrias;
- revestimentos;
- pintura; - pintura;
- armários; - marcenaria;
- climatização; - climatização;
- equipamentos; - sinais de obra improvisada.
- sinais de reforma improvisada.
Não resumir tudo como “reformado”. Uma reforma estética pode esconder sistemas antigos. “Reformado” não comprova renovação dos sistemas.
### 3.3 Custos de adequação ### Conforto
Estimar: - insolação;
- reparos imediatos;
- intervenções em até dois anos;
- mobiliário obrigatório;
- eletrodomésticos;
- mudança;
- custo de desocupação, quando aplicável;
- custo de tempo e transtorno;
- contingência de obra.
Aplicar margem de segurança para reformas. Não usar apenas o orçamento mais otimista.
### 3.4 Conforto ambiental
Avaliar em horários diferentes:
- incidência solar;
- calor; - calor;
- ventilação; - ventilação;
- iluminação; - ruído interno e externo;
- ruído;
- odores; - odores;
- privacidade; - privacidade;
- proximidade de vias; - drenagem;
- fluxo de pedestres;
- iluminação noturna;
- segurança percebida; - segurança percebida;
- drenagem e risco de alagamento. - internet e sinal de celular.
### 3.5 Liquidez e revenda
Considerar:
- tipologia;
- padrão da região;
- facilidade de financiamento;
- regularidade documental;
- garagem;
- elevador;
- posição;
- conservação;
- faixa de preço;
- público comprador provável;
- excesso de personalização;
- limitações permanentes de planta.
---
## 4. Cruzeiro Novo
### 4.1 Pontos a registrar ### Custos de adequação
- quadra; Estimar:
- bloco;
- posição no bloco;
- andar;
- orientação solar;
- vista;
- proximidade de vias e comércio;
- ruído;
- elevador;
- garagem ou estacionamento;
- estado das áreas comuns;
- taxa de condomínio;
- obras previstas;
- idade e conservação do prédio;
- prumadas;
- elétrica;
- fachada;
- acessibilidade;
- área privativa e área total;
- número original de quartos;
- alterações de planta;
- ocupação atual;
- situação documental.
### 4.2 Dois versus três quartos
No Cruzeiro, apartamentos anunciados como dois ou três quartos podem ter áreas semelhantes.
A análise deve verificar:
- planta original;
- quarto revertido;
- dependência transformada;
- sala dividida;
- ventilação do cômodo adicional;
- dimensão real dos quartos;
- impacto na circulação;
- valor de revenda.
Não classificar automaticamente três quartos como superior se o terceiro cômodo for inadequado.
### 4.3 Elevador e garagem
Ausência de elevador ou garagem é uma concessão relevante, não um detalhe.
Registrar:
- andar;
- número de lances de escada;
- acessibilidade futura;
- facilidade de mudança;
- uso por idosos;
- disponibilidade real de estacionamento;
- segurança ao estacionar;
- regras do condomínio.
O efeito no preço deve ser analisado com imóveis comparáveis.
### 4.4 Reforma
Imóveis antigos podem exigir modernização invisível no anúncio.
Durante a visita, verificar:
- quadro elétrico; - reparo imediato;
- aterramento; - obra em até dois anos;
- tomadas; - móveis e eletrodomésticos indispensáveis;
- tubulações; - regularização;
- registros; - mudança;
- pressão de água; - contingência;
- vazamentos; - tempo e transtorno.
- esquadrias;
- ruído entre unidades;
- estado da laje;
- gás;
- ventilação de banheiros;
- infiltrações em fachadas;
- histórico de obras do bloco.
--- ---
## 5. Jardins Mangueiral ## 4. Jardins Mangueiral
### 5.1 Filtro de tipologia
Somente considerar casas com **planta de três quartos**. ### 4.1 Planta original
Casas originalmente de dois quartos devem ser excluídas, mesmo quando reformadas ou anunciadas como tendo três quartos. Somente casas originalmente de três quartos.
A validação pode usar: Validar por:
- planta original; - planta padrão;
- implantação;
- posição da escada; - posição da escada;
- dimensões; - dimensões;
- fachada; - fachada;
- implantação;
- documentos; - documentos;
- comparação com unidades padrão; - fotos;
- fotografias. - comparação com unidades conhecidas.
### 5.2 Quintal fechado e ampliações Quarto adaptado não transforma uma planta original de dois quartos em candidata válida.
A perda do quintal original não é automaticamente um problema. ### 4.2 Quintal e ampliação
A conversão deve ser considerada positiva quando cria: Conversão positiva:
- sala ampliada; - sala ampliada;
- espaço de convivência;
- ambiente de refeições; - ambiente de refeições;
- espaço de convivência;
- escritório; - escritório;
- área social ventilada; - armazenamento útil;
- integração útil com a casa; - integração ventilada com a casa.
- armazenamento funcional.
Conversão negativa:
Penalizar quando a conversão:
- segunda cozinha redundante;
- duplica cozinha ou cooktop sem necessidade; - cooktop sem função clara;
- cria circulação inútil; - circulação de baixo valor;
- reduz ventilação e iluminação; - redução de luz e ventilação;
- aumenta calor; - calor excessivo;
- usa material de baixa qualidade; - impermeabilização duvidosa;
- compromete impermeabilização; - drenagem comprometida;
- dificulta manutenção; - manutenção difícil;
- cria ambiente sem função clara; - ocupação total sem ganho funcional.
- prejudica privacidade;
- ocupa todo o espaço sem compensação funcional. ### 4.3 Preferências funcionais confirmadas
### 5.3 Área gourmet - cozinha bem planejada, espaçosa e, idealmente, semi-integrada à área gourmet;
- área gourmet com churrasqueira deve participar do fluxo social da casa, não ficar isolada;
- priorizar casas conservadas e que exijam poucas intervenções antes da mudança;
- boa marcenaria, quartos e banheiros funcionais têm peso material;
- piscina, jacuzzi e banheira não são desejadas e reduzem fortemente a adequação;
- cubas de vidro são indesejadas por limpeza e aparência;
- penalizar marcenaria barata ou degradada, cozinha mal executada e área externa abandonada;
- fechamento em vidro exige teste rigoroso de calor e ventilação pelo risco de efeito estufa;
- quarto convertido em closet é uma concessão quando precisar ser revertido no futuro;
- placas solares são bônus, condicionadas à documentação e ao estado do sistema;
- detalhes estéticos reversíveis, como cortinas e sancas, pesam menos que planta, cozinha, conservação e área social.
### 4.4 Regularidade
Confirmar:
- cobertura e fechamento;
- drenagem e calhas;
- aprovação condominial;
- licenças quando aplicáveis;
- impacto em financiamento e seguro;
- área efetiva e conceito da área anunciada;
- placas solares e homologação;
- piscina, spa e impermeabilização.
Não atribuir valor automático ao rótulo “área gourmet”. ### 4.5 Ruído de vizinhança
Avaliar: Investigar:
- frequência provável de uso; - paredes compartilhadas;
- integração com a cozinha; - passos e móveis;
- redundância de equipamentos; - música;
- exaustão; - festas;
- ventilação; - cães;
- limpeza; - crianças;
- circulação; - área de lazer;
- capacidade de receber pessoas; - quadra esportiva;
- qualidade da execução; - comércio;
- manutenção. - motos;
- coleta de lixo;
Um segundo cooktop ao lado da cozinha principal pode ser redundante. - obras.
### 5.4 QCs e acesso a serviços ---
Relatos de moradores usados como referência anedótica: ## 5. Cruzeiro
- QCs 8 e 9: percebidas como mais fortes para acesso prático a comércio e serviços; ### 5.1 Planta e quartos
- QCs 6 e 7: alternativas consideradas;
- QCs 10 e 11: também consideradas boas.
Essas preferências não substituem verificação de campo. No estoque do Cruzeiro, dois e três quartos podem ter área semelhante e o terceiro cômodo pode ser conversão.
Para cada QC, medir ou registrar: Confirmar:
- distância a comércio; - planta original;
- acesso a mercado, farmácia e serviços; - dependência convertida;
- saída viária; - sala dividida;
- tempo em horários relevantes; - ventilação e dimensão do terceiro quarto;
- transporte; - impacto na circulação e revenda.
- topografia;
- iluminação;
- tráfego interno;
- ruído;
- estacionamento;
- áreas comuns;
- manutenção do condomínio.
### 5.5 Ruído e vizinhança O ranking corrente prioriza três quartos utilizáveis, mas a contagem anunciada não é suficiente.
Avaliar explicitamente: ### 5.2 Elevador, garagem e andar
- paredes compartilhadas; Ausência de elevador ou garagem é concessão relevante.
- ruído de passos e móveis;
- música;
- festas;
- cães;
- crianças;
- áreas de convivência;
- quadras esportivas;
- comércio;
- portarias;
- vias internas;
- motos;
- coleta de lixo;
- obras;
- templos;
- escolas;
- bares.
Fazer visitas em mais de um horário quando possível, incluindo noite ou fim de semana. Avaliar:
Perguntar aos moradores sem depender apenas do corretor. - número de lances de escada;
- acessibilidade futura;
- mudança e uso por idosos;
- vagas vinculadas ou apenas informais;
- disputa por estacionamento;
- segurança noturna;
- posição do apartamento.
### 5.6 Reformas e regularidade ### 5.3 Prédio e sistemas
Verificar: Verificar:
- fechamento de área externa; - quadro elétrico e aterramento;
- cobertura; - tubulações e registros;
- drenagem; - pressão de água;
- calhas; - gás;
- telhado; - prumadas;
- impermeabilização; - fachada;
- ventilação; - laje e infiltração;
- padrão elétrico; - obras previstas;
- hidráulica; - taxas extraordinárias;
- alterações estruturais; - estado das áreas comuns;
- aprovação condominial; - ruído entre unidades.
- regularidade documental;
- impacto em seguro e financiamento.
--- ---
## 6. Checklist de visita ## 6. Fotografias e anúncios
### Antes da visita As fotos fazem parte da evidência principal.
- salvar anúncio e data; Procedimento:
- registrar preço;
- registrar condomínio e IPTU;
- conferir área e quartos;
- buscar histórico do anúncio;
- verificar anúncios duplicados;
- mapear entorno;
- listar dúvidas.
### Durante a visita 1. abrir a primeira foto atual;
2. procurar selo de vendido, permuta ou negócio fechado;
- fotografar com autorização; 3. inspecionar galeria completa;
- registrar ruído com janelas abertas e fechadas; 4. comparar imagens com outros anúncios;
- testar torneiras, descargas, chuveiros e registros; 5. verificar coerência de planta, estado e área;
- observar umidade e odores; 6. identificar fotos antigas, decorativas ou de outro imóvel;
- verificar tomadas e quadro; 7. registrar qualidade e lacunas da evidência.
- medir cômodos críticos;
- conferir incidência solar;
- observar circulação;
- avaliar armazenamento;
- verificar sinal de celular e internet;
- examinar garagem ou estacionamento;
- conversar com porteiro ou moradores quando apropriado;
- perguntar motivo da venda;
- perguntar tempo de anúncio;
- perguntar reformas e documentação;
- verificar taxas extraordinárias.
### Depois da visita
- registrar impressão no mesmo dia; Republicações visualmente equivalentes contam como um imóvel provável.
- separar fatos de percepções;
- estimar custos;
- comparar com alternativas reais;
- listar riscos;
- definir preço máximo;
- registrar pendências documentais;
- evitar decisão motivada apenas por entusiasmo.
--- ---
## 7. Negociação ## 7. Checklist de visita
O preço máximo deve considerar: ### Antes
- valor comparável; - salvar anúncio, preço e data;
- estado; - revisar histórico;
- custos de compra; - verificar duplicatas;
- reforma; - mapear localização;
- liquidez; - listar pendências;
- concessões; - calcular custo total preliminar.
- risco;
- tempo de anúncio;
- histórico de preço;
- condição de pagamento;
- custo de oportunidade.
Não negociar com base apenas no percentual de desconto. ### Durante
O desconto nominal deve ser comparado ao custo real de corrigir as deficiências. - medir cômodos críticos;
- testar torneiras, registros e descargas;
- observar umidade e odores;
- conferir quadro elétrico;
- avaliar sol, calor e ventilação;
- medir ruído;
- conferir garagem ou estacionamento;
- perguntar motivo da venda e tempo de anúncio;
- pedir documentação e histórico de reformas;
- perguntar por taxas extraordinárias e conflitos de vizinhança.
--- ### Depois
## 8. Saída padrão por imóvel - registrar impressão no mesmo dia;
- separar fatos de entusiasmo;
Cada ficha analítica deve conter: - estimar custo de adequação;
- definir preço máximo;
- URL e identificador; - classificar: excluir, monitorar, aprofundar, visitar, negociar ou candidato forte.
- data de coleta;
- região, quadra, bloco ou QC;
- tipologia original;
- área;
- quartos;
- garagem;
- elevador;
- preço;
- condomínio;
- IPTU;
- preço por m² válido ou não;
- histórico de preço;
- possíveis duplicatas;
- pontos fortes;
- pontos fracos;
- reformas;
- custo estimado de adequação;
- riscos;
- pendências;
- posição relativa aos comparáveis;
- preço máximo sugerido, quando houver dados;
- decisão: excluir, monitorar, visitar, negociar ou candidato forte.

206
docs/SCRAPER_OPERATIONS.md

@ -0,0 +1,206 @@
# Operação do scraper e do banco
## 1. Arquitetura atual
Estrutura esperada:
```text
dfimoveis_data/
├── dfimoveis.sqlite3
├── photos/
│ └── <listing_id>/
├── raw/
│ ├── listing/
│ └── search/
└── browser-profile/
```
O SQLite é a fonte estruturada. Fotos e HTML bruto são evidências complementares essenciais.
---
## 2. Funções já previstas no scraper
- múltiplas pesquisas;
- paginação com `pagina=N`;
- requisições assíncronas;
- concorrência controlada;
- retentativas;
- extração de páginas de anúncio;
- download de todas as fotos;
- armazenamento em SQLite;
- observações históricas e mudanças;
- HTML bruto comprimido;
- marcação de anúncios removidos ou inativos;
- fallback opcional por Playwright.
Não simplificar o scraper de modo a perder histórico, fotos ou HTML bruto.
---
## 3. Cloudflare e 403
Foi confirmado desafio JavaScript do Cloudflare, com referências a `challenges.cloudflare.com` e `cf-ray` associado a GRU.
Consequências:
- `httpx` isolado não resolve desafio JavaScript;
- trocar apenas User-Agent, aumentar retries ou reduzir concorrência não garante acesso;
- o scraper deve tentar o cliente HTTP normal e usar Playwright como fallback quando houver 403 ou challenge.
### Playwright
Configuração operacional:
- Chromium visível no primeiro uso;
- perfil persistente em `dfimoveis_data/browser-profile`;
- reutilização de cookies e sessão;
- resolução manual do challenge quando necessário;
- depois, possibilidade de execução automatizada enquanto a sessão permanecer válida;
- não usar modo headless no primeiro desafio.
O objetivo é usar uma sessão legítima do navegador, não tentar derrotar ou explorar mecanismos de proteção.
Respeitar termos aplicáveis, limites de requisição e carga do site.
---
## 4. User-Agent e sessão
- usar `DFIMOVEIS_USER_AGENT` configurável;
- preservar cookies e perfil local;
- não gravar credenciais no repositório;
- não compartilhar o diretório `browser-profile`;
- incluir perfis e cookies no `.gitignore`.
---
## 5. Banco em análise
Caminho:
```text
dfimoveis_data/dfimoveis.sqlite3
```
Abrir em modo somente leitura:
```python
connection = sqlite3.connect(
"file:dfimoveis_data/dfimoveis.sqlite3?mode=ro",
uri=True,
)
connection.execute("PRAGMA query_only = ON")
```
Não executar análises que escrevam no banco.
Para transformações:
- exportar Parquet/CSV;
- criar `data/derived/analytics.sqlite`;
- ou trabalhar em uma cópia consistente.
---
## 6. Snapshot de 22/07/2026
- 162 anúncios;
- 637 observações históricas;
- 7.457 registros de foto: 3.521 da galeria primária e 3.936 evidências legadas excluídas da análise;
- Mangueiral: 95 anúncios acumulados, 78 ativos e 17 inativos;
- Cruzeiro Novo: 67 acumulados, 63 ativos e 4 inativos;
- coleta entre 13 e 22/07/2026;
- a partir da execução 21, somente o Mangueiral permanece no arquivo de buscas.
A cobertura é curta. Não inferir liquidez, sazonalidade ou tendência de preço.
O histórico legado contém mudanças infladas por campos textuais instáveis. O schema v2 usa `stable_content_hash`, exclui ruído de interface e registra mudanças materiais futuras em `listing_change_events`.
### Migração v2
- backup anterior: `dfimoveis_data/backups/dfimoveis_before_schema_v2_20260722.sqlite3`;
- migração idempotente: `python scripts/migrate_dfimoveis_schema_v2.py`;
- nunca apagar fotos legadas: usar `v_primary_photos` ou `is_primary_gallery=1` nas análises;
- usar `creci_normalized`, `address_normalized` e `neighborhood_normalized`, preservando os campos brutos;
- consultar `v_listing_current_availability` e `v_open_data_quality_issues` para triagem;
- futuras execuções registram buscas e configuração em `crawl_runs`.
---
## 7. Qualidade e deduplicação
O scraper e a análise devem preservar três níveis:
- anúncio;
- observação histórica;
- imóvel físico provável.
Sinais de duplicidade:
- imagens iguais ou muito semelhantes;
- mesma implantação;
- texto quase idêntico;
- mesmo endereço;
- mesmos quartos, área e preço;
- anúncios de corretores diferentes.
Não apagar duplicatas. Registrar grupos, confiança e anúncio de referência.
---
## 8. Disponibilidade e “vendido”
Auditar:
- primeira foto;
- descrição;
- status estruturado;
- atividade na coleta.
Estados sugeridos:
- ativo;
- inativo;
- vendido visualmente indicado;
- negócio fechado declarado;
- republicado;
- duplicata provável;
- status desconhecido.
Selo visual é evidência operacional. Não prova transação registral.
---
## 9. Observabilidade
Cada execução deve registrar:
- início e fim;
- versão do scraper;
- configuração;
- pesquisas executadas;
- páginas solicitadas e concluídas;
- status HTTP;
- desafios Cloudflare;
- anúncios vistos;
- fotos baixadas;
- erros;
- tempo de execução;
- número de anúncios marcados inativos.
Diferenciar falha de coleta de redução real de estoque.
---
## 10. Segurança operacional
- não versionar banco, fotos, HTML bruto, cookies ou perfil de navegador;
- não expor tokens e sessões;
- limitar concorrência;
- usar backoff;
- evitar coleta agressiva;
- não remover dados históricos;
- manter backup antes de migrações;
- testar mudanças em banco de desenvolvimento;
- nunca migrar o banco original durante uma análise.

55
reports/creci_26903_mangueiral/README.md

@ -0,0 +1,55 @@
# Imóveis do CRECI 26.903 — Jardins Mangueiral
**Snapshot:** 21/07/2026
**Fonte:** `dfimoveis_data/dfimoveis.sqlite3`, aberto somente para leitura.
## Pergunta
Quais anúncios de imóveis no Jardins Mangueiral estão associados ao CRECI **26.903**, separados entre vendidos e disponíveis?
## Método e ressalva
O portal apresenta o registro como `26.903`. O campo `creci` foi normalizado para a busca removendo pontos e espaços, porque seu conteúdo bruto inclui texto residual da página. Foram encontrados **15 anúncios acumulados**, todos ainda marcados como ativos pelo coletor no snapshot.
A separação abaixo usa a primeira foto corrente:
- **vendido:** há selo explícito “VENDIDO”, “negócio fechado” ou equivalente;
- **disponível:** anúncio ativo e sem selo visual de venda na primeira foto.
“Disponível” significa apenas **aparentemente disponível no portal**. É necessário confirmar diretamente com o anunciante. Da mesma forma, o selo de vendido é evidência operacional, não prova registral da transação.
## Vendidos — 8 anúncios
| Anúncio | QC | Preço anunciado | Área estruturada | Evidência |
|---:|---:|---:|---:|---|
| [1366801](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1366801) | 11 | R$ 575 mil | 68 m² | selo na primeira foto |
| [1346017](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1346017) | 10 | R$ 577 mil | 68 m² | selo na primeira foto |
| [1349519](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1349519) | 14 | R$ 640 mil | 68 m² | selo na primeira foto |
| [1356197](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-4-1356197) | 4 | R$ 649 mil | 68 m² | selo na primeira foto |
| [1346231](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1346231) | 10 | R$ 660 mil | 68 m² | selo na primeira foto |
| [1352943](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-3-1352943) | 3 | R$ 670 mil | 68 m² | selo na primeira foto |
| [1351729](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-9-1351729) | 9 | R$ 680 mil | 68 m² | selo na primeira foto |
| [1362968](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-13-1362968) | 13 | R$ 730 mil | 68 m² | selo na primeira foto |
## Aparentemente disponíveis — 7 anúncios
| Anúncio | QC | Preço anunciado | Área estruturada | Situação no snapshot |
|---:|---:|---:|---:|---|
| [1173275](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-15-1173275) | 15 | R$ 549 mil | 68 m² | ativo, sem selo de venda |
| [1336887](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-condominio-jardins-mangueiral-1336887) | não informado | R$ 550 mil | 65 m² | ativo, sem selo de venda |
| [1363016](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1363016) | 14 | R$ 550 mil | 68 m² | ativo, sem selo de venda |
| [1345134](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1345134) | 11 | R$ 640 mil | 68 m² | ativo, sem selo de venda |
| [1324014](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-12-rua-k-1324014) | 12 | R$ 649 mil | 68 m² | ativo, sem selo de venda |
| [1358920](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-13-1358920) | 13 | R$ 675 mil | 68 m² | ativo, sem selo de venda |
| [1278187](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1278187) | 10 | R$ 690 mil | 68 m² | ativo, sem selo de venda |
## Leitura prática
- O CRECI 26.903 concentra **8 de 15 anúncios com indicação visual de venda** no snapshot, ou 53,3% do recorte.
- Os oito continuam ativos tecnicamente no portal; portanto, o estado estruturado isolado superestima a oferta desse anunciante.
- Não é possível concluir velocidade de venda: o banco começou a observar esses anúncios em 13/07/2026, e não contém a data da negociação.
- Os anúncios podem representar estoque captado pelo mesmo corretor ou imobiliária, não necessariamente imóveis de sua propriedade.
## Reprodução
A consulta usada está em [`query.sql`](query.sql). A classificação visual está registrada em [`results.csv`](results.csv) e não é inferida pelo SQL.

15
reports/creci_26903_mangueiral/query.sql

@ -0,0 +1,15 @@
-- Executar exclusivamente em conexão read-only.
SELECT
listing_id,
price_brl,
area_m2,
bedrooms,
address,
inactive_at,
first_seen_at,
last_seen_at,
url
FROM listings
WHERE lower(url) LIKE '%jardins-mangueiral%'
AND creci_normalized = '26903'
ORDER BY price_brl, listing_id;

16
reports/creci_26903_mangueiral/results.csv

@ -0,0 +1,16 @@
listing_id,classification,qc,price_brl,area_m2,database_active,first_photo_sale_marker,url
1366801,sold_visual,11,575000,68,true,true,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1366801
1346017,sold_visual,10,577000,68,true,true,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1346017
1349519,sold_visual,14,640000,68,true,true,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1349519
1356197,sold_visual,4,649000,68,true,true,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-4-1356197
1346231,sold_visual,10,660000,68,true,true,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1346231
1352943,sold_visual,3,670000,68,true,true,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-3-1352943
1351729,sold_visual,9,680000,68,true,true,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-9-1351729
1362968,sold_visual,13,730000,68,true,true,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-13-1362968
1173275,apparently_available,15,549000,68,true,false,https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-15-1173275
1336887,apparently_available,,550000,65,true,false,https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-condominio-jardins-mangueiral-1336887
1363016,apparently_available,14,550000,68,true,false,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1363016
1345134,apparently_available,11,640000,68,true,false,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1345134
1324014,apparently_available,12,649000,68,true,false,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-12-rua-k-1324014
1358920,apparently_available,13,675000,68,true,false,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-13-1358920
1278187,apparently_available,10,690000,68,true,false,https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1278187
1 listing_id classification qc price_brl area_m2 database_active first_photo_sale_marker url
2 1366801 sold_visual 11 575000 68 true true https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1366801
3 1346017 sold_visual 10 577000 68 true true https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1346017
4 1349519 sold_visual 14 640000 68 true true https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1349519
5 1356197 sold_visual 4 649000 68 true true https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-4-1356197
6 1346231 sold_visual 10 660000 68 true true https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1346231
7 1352943 sold_visual 3 670000 68 true true https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-3-1352943
8 1351729 sold_visual 9 680000 68 true true https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-9-1351729
9 1362968 sold_visual 13 730000 68 true true https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-13-1362968
10 1173275 apparently_available 15 549000 68 true false https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-15-1173275
11 1336887 apparently_available 550000 65 true false https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-condominio-jardins-mangueiral-1336887
12 1363016 apparently_available 14 550000 68 true false https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1363016
13 1345134 apparently_available 11 640000 68 true false https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1345134
14 1324014 apparently_available 12 649000 68 true false https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-12-rua-k-1324014
15 1358920 apparently_available 13 675000 68 true false https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-13-1358920
16 1278187 apparently_available 10 690000 68 true false https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1278187

172
reports/cruzeiro_top10/README.md

@ -1,172 +0,0 @@
# Ranking preliminar — apartamentos no Cruzeiro Novo
**Data de referência do snapshot:** 17/07/2026
**Relatório revisado:** 17/07/2026
**Unidade principal:** imóvel físico provável, não snapshot nem anúncio isolado
**Finalidade:** priorizar visitas; não estimar preço negociado nem substituir vistoria
## Pergunta
Quais são os dez imóveis do Cruzeiro Novo que mais merecem atenção segundo as preferências e regras do projeto, dando às fotografias importância comparável à dos dados estruturados?
## Universo e método
Foram avaliados os 65 anúncios encontrados pela busca:
`https://www.dfimoveis.com.br/venda/df/cruzeiro/novo/apartamento/3-quartos`
A coleta cobre um único dia e um único portal. O banco foi aberto somente em leitura. Os campos foram complementados com os HTMLs brutos, pois localização e descrição no SQLite têm limitações já documentadas em `docs/ACTUAL_SCHEMA.md`.
Na coleta concluída em 17/07, os 65 anúncios do Cruzeiro permaneceram ativos e nenhum anúncio novo entrou. O único preço alterado foi o do anúncio `1372539`, de R$ 572.500 para R$ 569.000; ele continua fora do ranking porque a descrição confirma planta de três quartos convertida em dois. Assim, a ordem foi mantida após revisão das mudanças materiais.
Não foi criada pontuação numérica, pois os pesos não foram aprovados. A ordem é uma **prioridade de visita ajustada por risco**, aplicando:
1. critérios eliminatórios e inconsistências;
2. planta funcional de três quartos;
3. indícios de reforma relevante ou custos escondidos;
4. iluminação, ventilação, distribuição e conservação visíveis;
5. andar, elevador e garagem;
6. preço, área e condomínio anunciados;
7. qualidade e completude da evidência.
### Revisão fotográfica
Em 17/07/2026, a primeira foto dos dez escolhidos foi auditada especificamente para marcas de “vendido” ou “negócio fechado”. Nenhuma das dez apresentou esse sinal.
A primeira passagem examinou até 16 imagens de cada um dos 65 anúncios, totalizando 1.029 arquivos. Depois, foram examinadas todas as 532 fotos dos 17 finalistas. Descontada a sobreposição entre as etapas, 1.289 arquivos distintos foram efetivamente vistos.
As imagens foram usadas para observar conservação, coerência da reforma, luz, ventilação aparente, tamanho relativo dos quartos, circulação, integração social, cozinha, banheiros, área de serviço, fachada e estacionamento. Fotografias não permitem validar elétrica, hidráulica, infiltração, ruído, estrutura ou documentação.
Muitos anúncios incluem, depois das fotos do imóvel, imagens genéricas do portal, logos e até cartões de outros imóveis marcados como “vendido”. Esses ativos foram desconsiderados na avaliação visual do apartamento.
## Ranking
### 1. Anúncio 1345146 — SHCES 1207, bloco A
- **Anunciado:** R$ 670.000; 65 m²; condomínio R$ 605; 3 quartos, 1 suíte; 4º andar.
- **Por que lidera:** é o conjunto mais equilibrado entre reforma pronta, planta preservada e acessibilidade. O anúncio declara elevador, diferencial forte para uso de longo prazo.
- **Fotos:** reforma coerente e bem conservada; cozinha planejada, área de serviço separada, dois banheiros atualizados, três quartos reconhecíveis e boa luz. Os dormitórios e a área de serviço parecem compactos, mas funcionais.
- **Pendências:** confirmar funcionamento/obras do elevador, posição solar, área da matrícula, estacionamento/garagem e sistemas elétrico e hidráulico.
- **Ação:** candidato forte; visitar primeiro.
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1207-bloco-a-1345146
### 2. Anúncio 1232283 — SHCES 309, bloco B
- **Anunciado:** R$ 600.000; 70 m²; condomínio R$ 497; 3 quartos, 1 suíte; 3º andar; 2 vagas; nascente e vazado.
- **Por que está no topo:** maior área entre os primeiros colocados, preço competitivo, duas vagas anunciadas e reforma recente. O anúncio informa vista livre e ocupação imediata.
- **Fotos:** ambientes claros, armários abundantes, dois banheiros modernos e reforma consistente. Um dos dormitórios parece estreito; a qualidade da circulação precisa ser sentida presencialmente.
- **Pendências:** confirmar se as duas vagas são privativas/vinculadas e se há elevador — ele não é informado. No 3º andar, sua ausência é concessão forte.
- **Ação:** candidato forte; visitar, condicionado à confirmação das vagas e acessibilidade.
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-309-bloco-b-1232283
### 3. Anúncio 1357415 — SHCES 1109
- **Anunciado:** R$ 630.000; 67,2 m²; condomínio R$ 350; 3 quartos, 1 suíte; 4º andar; nascente e vazado.
- **Por que se destaca:** reforma visualmente mais homogênea que a maioria, bloco revitalizado, vista livre, boa ventilação e condomínio anunciado baixo.
- **Fotos:** sala social bem resolvida, cozinha planejada, marcenaria útil, dois banheiros atualizados, quartos claros e conservação convincente. Não aparecem sinais evidentes de obra imediata.
- **Pendências:** não há elevador informado e o imóvel está no 4º andar; confirmar isso antes da visita. Verificar estacionamento, escopo real da reforma e autenticidade do condomínio de R$ 350.
- **Ação:** candidato forte se a escada for aceitável; caso contrário, rebaixar fortemente.
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-1357415
### 4. Anúncio 1357633 — SHCES 309
- **Anunciado:** R$ 600.000; 64 m²; 3 quartos; 2º andar; nascente, vazado e de canto.
- **Por que se destaca:** o anúncio declara reforma de elétrica, hidráulica, pisos e acabamentos, além de documentação regular. O 2º andar reduz o impacto provável da ausência de elevador.
- **Fotos:** acabamento moderno e uniforme, boa marcenaria, iluminação natural e três dormitórios aparentes. A planta permanece convencional e funcional; não há suíte anunciada, mas há banheiro de serviço.
- **Pendências:** condomínio, IPTU, garagem e elevador estão ausentes. Confirmar notas/escopo da reforma e ventilação dos banheiros.
- **Ação:** candidato forte; visitar.
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-309-1357633
### 5. Anúncio 1376958 — SHCES 911, bloco B
- **Anunciado:** R$ 599.000; 62 m²; condomínio R$ 600; 3 quartos, 1 suíte; 2º andar; nascente e vazado.
- **Por que se destaca:** combina preço baixo no grupo reformado, andar razoável, suíte e ventilação cruzada declarada.
- **Fotos:** sala ampla para a metragem, cozinha e banheiros atualizados, boa vista e luz. O quarto com beliche aparenta ser pequeno, mas utilizável; a área de serviço é funcional, embora simples.
- **Pendências:** prédio sem elevador segundo a descrição e sem garagem informada. Confirmar área privativa, ruído da via visível e o que está incluído no condomínio.
- **Ação:** visitar.
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-b-1376958
### 6. Anúncio 1345903 — SHCES 1303
- **Anunciado:** R$ 565.000; 64,5 m²; condomínio R$ 529; 3 quartos, 1 suíte; 1º andar; vazado.
- **Por que se destaca:** menor preço do top 10, acessibilidade melhor por estar no 1º andar, suíte e informação explícita de elétrica e esquadrias novas.
- **Fotos:** imóvel cuidado e aparentemente habitável sem obra imediata. Banheiros estão atualizados, mas cozinha, pisos e parte da marcenaria têm linguagem mais antiga. A planta parece íntegra e os três quartos são visíveis.
- **Pendências:** somente estacionamento público; confirmar ventilação, ruído do 1º andar, hidráulica e segurança das janelas.
- **Ação:** visitar com foco em custo real de modernização opcional.
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1303-1345903
### 7. Anúncio 1339324 — SHCES 703
- **Anunciado:** R$ 665.000; 64 m²; condomínio R$ 390; 3 quartos, 1 suíte; 3º andar.
- **Por que se destaca:** reforma pronta, climatização, condomínio anunciado baixo e excelente aproveitamento de marcenaria.
- **Fotos:** uma das melhores apresentações: integração social coerente, cozinha funcional, dois banheiros bem executados, três quartos utilizáveis, boa iluminação e áreas comuns cuidadas. A circulação íntima parece estreita.
- **Pendências:** elevador, garagem, posição solar e escopo técnico da reforma não são informados. O 3º andar sem elevador seria concessão relevante.
- **Ação:** visitar após esclarecer acessibilidade e estacionamento.
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-703-1339324
### 8. Imóvel provável dos anúncios 1370889 e 1375200 — SHCES 207
- **Anunciado:** R$ 695.000; 62 m²; condomínio entre R$ 435 e R$ 453; 3 quartos, 1 suíte. Um anúncio informa 2º andar.
- **Por que se destaca:** prédio reformado, planta social integrada, suíte e condição pronta para morar.
- **Fotos:** os dois anúncios mostram o mesmo apartamento com alta confiança visual. Sala/cozinha integradas funcionam bem, há área de serviço independente, dois banheiros atualizados e três dormitórios. A reforma parece consistente, embora bastante personalizada.
- **Pendências:** confirmar formalmente que os anúncios são da mesma unidade, andar, orientação solar, área, garagem e elevador. A duplicidade não deve inflar a oferta.
- **Ação:** visitar uma única vez após confirmar o anúncio primário.
- **URLs:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1370889 e https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1375200
### 9. Anúncio 1372268 — SHCES 305, bloco I
- **Anunciado:** R$ 595.000; 62,61 m²; condomínio R$ 460; 3 quartos; 1º andar; canto e vazado; 2 vagas.
- **Por que entra:** duas vagas anunciadas e 1º andar são diferenciais duráveis. O preço deixa margem maior para atualização que os reformados mais caros.
- **Fotos:** planta convencional, clara e aparentemente conservada; cozinha e banheiros são funcionais, porém mais antigos. Não há sinal visual de obra estrutural, mas a modernização seria maior do que nos primeiros colocados.
- **Pendências:** confirmar documentalmente as duas vagas, ventilação, elétrica, hidráulica e orçamento real de adequação.
- **Ação:** visitar como alternativa de valor; não assumir que o desconto cobre reforma sem orçamento.
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-bloco-i-1372268
### 10. Anúncio 1373131 — SHCES 305
- **Anunciado:** R$ 725.000; 65 m²; condomínio R$ 420; 3 quartos; 2º andar; nascente e vazado; sem elevador.
- **Por que entra:** é a reforma tecnicamente mais bem descrita: elétrica e hidráulica renovadas, projeto de arquiteta, boa orientação e ventilação cruzada.
- **Fotos:** execução visual limpa e consistente, cozinha e banheiro novos, três quartos preservados e boa luminosidade. Não possui suíte anunciada e a linguagem totalmente branca deve ser avaliada sem o efeito de grande-angular.
- **Pendências:** não há garagem informada; preço é alto para unidade sem suíte, elevador ou vaga. Confirmar documentação da reforma e medidas reais dos ambientes.
- **Ação:** visitar somente se a condição pronta justificar o prêmio pedido.
- **URL:** https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-1373131
## Quase entraram
- **[1382466](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-101-bloco-c-1382466), R$ 650.000:** boa reforma aparente e uma vaga, mas faltam fotos da cozinha e informações de andar, elevador e orientação; aprofundar antes de substituir alguém do top 10.
- **[1290510](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-a-1290510), R$ 640.000:** três quartos com suíte, vazado e reformado, mas fica no 4º andar sem elevador informado e os valores de condomínio/IPTU foram claramente extraídos como R$ 1; precisa de confirmação.
- **[1371813](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-bloco-d-1371813), R$ 709.000:** reforma moderna e 68 m², mas o conjunto de fotos é curto e não esclarece bem três quartos, área de serviço, andar, garagem ou elevador.
## Quarentenas e exclusões relevantes
- **1373346:** é um imóvel no Noroeste capturado indevidamente pela busca; excluído.
- **1212351, 1338088, 1259968, 1315328 e 1372539:** planta de três quartos convertida em dois ou sala/escritório; não priorizados para residência de longo prazo.
- **1313649:** anunciado na descrição como dois quartos apesar do campo estruturado indicar três; condomínio de R$ 1.172; excluído até esclarecer planta.
- **1370861:** três quartos originais transformados em suíte master e escritório; não atende à funcionalidade desejada.
- **1365929, 1161400, 1278826 e similares:** fotos e descrição indicam reforma relevante, sem desconto claramente suficiente frente às alternativas prontas.
- **1366910, 1372290 e 1383534:** 22–35 fotos visualmente similares indicam o mesmo imóvel provável, mas os anúncios divergem entre SHCES 207 e SHCES 303 e entre 63 e 74 m². O conjunto seria competitivo, porém permanece em quarentena até confirmar endereço, área e anúncio válido.
- **1370948, 1371797 e 1372167:** grupo provável de anúncios duplicados, sem vantagem suficiente sobre o top 10.
- **1357834 e 1358242:** duplicata provável; há ainda um terceiro anúncio visualmente semelhante, 1382413.
- **1372907, 1348535 e 1380850:** elevador, garagem ou área podem ser atrativos, mas os preços de R$ 820 mil a R$ 990 mil não vêm acompanhados de vantagem funcional suficiente para superar as opções anteriores. A conclusão financeira depende de atualização dos saldos e limites do usuário.
Uma imagem com selo “vendido” foi tratada como sinal de inconsistência, não como prova de venda, porque muitos anúncios contêm ativos genéricos de outros imóveis.
## Limitações e verificações para visita
O ranking é sensível a cinco informações ainda não confirmadas: tolerância pessoal a escadas, existência jurídica das vagas, conceito correto de área, qualidade invisível das reformas e limite financeiro atual. Antes de qualquer proposta:
1. conferir matrícula, área privativa e vagas;
2. testar elétrica, hidráulica, pressão, esquadrias e sinais de infiltração;
3. medir os três quartos e validar que permanecem funcionais;
4. visitar em horário de tráfego e à noite para ruído e estacionamento;
5. verificar elevador, obras e atas do condomínio;
6. confirmar condomínio, IPTU, taxas extras e orientação solar;
7. comparar preço máximo somente depois de atualizar recursos, FGTS, reserva e prestação confortável.
## Reprodutibilidade
- `analysis.py`: extrai banco e HTMLs em leitura, produzindo TSV.
- `photo_similarity.py`: sinaliza duplicatas por hash visual simples; os resultados exigem revisão humana.
- `query.sql`: consultas principais de universo e qualidade.
- `parameters.json`: parâmetros e fingerprint do snapshot.
- `results.csv`: ranking em formato tabular.

BIN
reports/cruzeiro_top10/__pycache__/analysis.cpython-314.pyc

Binary file not shown.

BIN
reports/cruzeiro_top10/__pycache__/photo_similarity.cpython-314.pyc

Binary file not shown.

22
reports/cruzeiro_top10/parameters.json

@ -1,22 +0,0 @@
{
"analysis_date": "2026-07-17",
"listing_data_reference_date": "2026-07-17",
"database_path": "dfimoveis_data/dfimoveis.sqlite3",
"database_main_file_sha256": "7a5102a62bc66a344bd9440fa36301d10a57711bcdd263b767b65e88f70957f4",
"logical_snapshot_sha256": "fe604aab8af7f67c833e6de2a0fc81c7dc2950ca440ad7b23ebd03d0c2546188",
"database_mode": "read_only_transaction_with_wal",
"search_url_fragment": "/cruzeiro/novo/",
"listing_limit": 100,
"observed_listing_count": 65,
"database_crawl_run_count": 16,
"active_listing_count": 65,
"new_listing_count_since_previous_report": 0,
"material_price_change_count": 1,
"ranking_unit": "probable_physical_property",
"numeric_score_used": false,
"photo_first_pass_limit_per_listing": 16,
"photo_finalist_count": 17,
"photo_files_reviewed_distinct": 1289,
"first_photo_sale_marker_audit_count": 10,
"visually_unavailable_listing_ids": []
}

11
reports/cruzeiro_top10/results.csv

@ -1,11 +0,0 @@
rank,representative_listing_id,probable_duplicate_ids,price_brl,area_m2,bedrooms,suites,floor,elevator,parking_spaces,decision
1,1345146,,670000,65,3,1,4,yes,,candidato forte
2,1232283,,600000,70,3,1,3,unknown,2,candidato forte
3,1357415,,630000,67.2,3,1,4,unknown,,candidato forte condicionado a escadas
4,1357633,,600000,64,3,0,2,unknown,,visitar
5,1376958,,599000,62,3,1,2,no,,visitar
6,1345903,,565000,64.5,3,1,1,unknown,0,visitar
7,1339324,,665000,64,3,1,3,unknown,,visitar após esclarecer acessibilidade
8,1370889,1375200,695000,62,3,1,2,unknown,,visitar uma única vez
9,1372268,,595000,62.61,3,0,1,unknown,2,visitar como alternativa de valor
10,1373131,,725000,65,3,0,2,no,,visitar se o prêmio pela reforma for aceitável
1 rank representative_listing_id probable_duplicate_ids price_brl area_m2 bedrooms suites floor elevator parking_spaces decision
2 1 1345146 670000 65 3 1 4 yes candidato forte
3 2 1232283 600000 70 3 1 3 unknown 2 candidato forte
4 3 1357415 630000 67.2 3 1 4 unknown candidato forte condicionado a escadas
5 4 1357633 600000 64 3 0 2 unknown visitar
6 5 1376958 599000 62 3 1 2 no visitar
7 6 1345903 565000 64.5 3 1 1 unknown 0 visitar
8 7 1339324 665000 64 3 1 3 unknown visitar após esclarecer acessibilidade
9 8 1370889 1375200 695000 62 3 1 2 unknown visitar uma única vez
10 9 1372268 595000 62.61 3 0 1 unknown 2 visitar como alternativa de valor
11 10 1373131 725000 65 3 0 2 no visitar se o prêmio pela reforma for aceitável

64
reports/cruzeiro_top15/README.md

@ -0,0 +1,64 @@
# Top 15 — Cruzeiro Novo
**Snapshot:** 21/07/2026
**Objetivo:** priorizar investigação para moradia de 10–20 anos, mantendo o Cruzeiro como alternativa secundária e oportunística; preço anunciado não é preço negociado.
## Universo e método
O banco `dfimoveis_data/dfimoveis.sqlite3` foi aberto em modo somente leitura (`mode=ro`, `PRAGMA query_only=ON`). O snapshot contém 161 anúncios, 562 observações históricas e 7.363 fotos; no recorte do Cruzeiro Novo há 67 anúncios acumulados, 63 ativos e 4 inativos. A coleta cobre 13–21/07/2026, período curto demais para inferir liquidez.
Em 21/07 entraram `1381514` e `1400223`; `1375199` e `1383814` ficaram inativos. O primeiro novo anúncio é reformado, mas está no 4º andar e não informa elevador ou vaga. O segundo custa R$ 495 mil e está no 2º andar, porém as fotos mostram necessidade de reforma ampla. Ambos foram incluídos como investigações condicionadas, não como candidatos fortes.
O ranking considera três quartos utilizáveis, funcionalidade, condição aparente, iluminação/ventilação, acessibilidade futura, elevador, natureza da vaga, custo de adequação e qualidade da evidência. Não usa preço por m², pois área e qualidade dos campos não são uniformes. Republicações visualmente equivalentes contam como um imóvel provável. A falta de informação é marcada como pendência, nunca como aprovação.
## Auditoria da primeira foto
A primeira foto corrente dos finalistas, dos dois novos anúncios e de `1263868` foi aberta e inspecionada para “vendido”, “negócio fechado” ou equivalente. O anúncio `1263868`, ainda ativo no banco, continua com **VENDIDO** na foto principal e permanece em quarentena. Nenhum dos 15 colocados apresenta esse indicativo. A imagem é evidência operacional de indisponibilidade, não prova registral da venda.
A triagem perceptual foi reexecutada com Pillow 12.3.0 e confirmou os principais grupos de republicação já documentados. Entre os finalistas, `1348535` teve somente três fotos semelhantes às de `1370861`; os endereços distintos (SHCES 1307 e 913) não sustentam deduplicação. O ranking permaneceu inalterado.
Todos os classificados continuam com a tipologia original de três quartos apenas como **provável e pendente de confirmação**. A contagem estruturada, isoladamente, não prova que o terceiro quarto seja original e funcional.
## Ranking
| # | Anúncio | Local | Preço | Área informada | Por que priorizar | Principal pendência |
|---:|---|---|---:|---:|---|---|
| 1 | [1345146](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1207-bloco-a-1345146) | SHCES 1207 A | R$ 670 mil | 65 m² | Reforma coerente, suíte e elevador anunciado | Vaga, matrícula e sistemas |
| 2 | [1357415](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-1357415) | SHCES 1109 | R$ 630 mil | 67,2 m² | Boa reforma, suíte, nascente e vazado | Confirmar elevador |
| 3 | [1357633](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-309-1357633) | SHCES 309 | R$ 600 mil | 64 m² | Reforma uniforme e sistemas renovados declarados | Elevador, garagem e comprovação da obra |
| 4 | [1376958](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-b-1376958) | SHCES 911 B | R$ 599 mil | 62 m² | Reformado, nascente, vazado e suíte | Sem elevador; garagem não informada |
| 5 | [1345903](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1303-1345903) | SHCES 1303 | R$ 565 mil | 64,5 m² | Primeiro andar e preço competitivo | Sem garagem; ruído e segurança |
| 6 | [1339324](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-703-1339324) | SHCES 703 | R$ 665 mil | 64 m² | Integração social, suíte e boa apresentação | Andar, elevador e garagem |
| 7 | [1370889](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1370889) / [1375200](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1375200) | SHCES 207 | R$ 695 mil | 62 m² | Reformado e com suíte | Duplicata provável; andar, elevador e vaga |
| 8 | [1372268](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-bloco-i-1372268) | SHCES 305 I | R$ 595 mil | 62,61 m² | Primeiro andar e duas vagas anunciadas | Provar vagas e orçar modernização |
| 9 | [1373131](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-1373131) | SHCES 305 | R$ 725 mil | 65 m² | Reforma tecnicamente bem descrita | Sem elevador/garagem e preço alto |
| 10 | [1381514](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-301-1381514) | SHCES 301 | R$ 690 mil | 63 m² estruturados; 93 m² nos detalhes | Reforma completa aparente, boa marcenaria e três quartos fotografados | 4º andar; elevador, vaga e conceito de área não informados |
| 11 | [1382466](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-101-bloco-c-1382466) | SHCES 101 C | R$ 650 mil | 62 m² | Boa reforma aparente e uma vaga | Galeria curta; andar e elevador ausentes |
| 12 | [1290510](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-a-1290510) | SHCES 911 A | R$ 640 mil | 62 m² | Suíte, vazado e reformado | 4º andar; condomínio/IPTU extraídos como R$ 1 |
| 13 | [1371813](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-bloco-d-1371813) | SHCES 1109 D | R$ 709 mil | 68 m² | Reforma moderna e área acima do padrão | Fotos não comprovam toda a planta, vaga ou elevador |
| 14 | [1400223](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-609-bloco-d-1400223) | SHCES 609 D | R$ 495 mil | 62,61 m² | 2º andar, boa luz e preço com margem para obra | Reforma ampla, vaga/elevador ausentes e sistemas antigos |
| 15 | [1369327](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-203-bloco-g-1369327) | SHCES 203 G | R$ 490 mil | 62 m² | Vaga e preço com margem para adequação | Estado interno, 4º andar e ausência de elevador |
## Quase entraram
- [1375154](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1205-1375154), R$ 550 mil: 67,2 m² e boa luminosidade, mas pede modernização e confirmação de andar/vaga.
- [1370890](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-105-1370890), R$ 560 mil: primeiro andar e DCE, porém condição mais básica e sem garagem informada.
- [1368610](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-309-1368610), R$ 579,9 mil: boa distribuição e melhorias declaradas; falta vantagem clara frente aos classificados.
- [1370891](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1370891), R$ 520 mil: vazado e de preço baixo, mas sem vaga e ainda dependente de modernização; saiu apenas pela entrada de evidência nova.
## Quarentenas e limitações
- `1232283` e `1345890` ficaram inativos em 20/07; `1375199` e `1383814`, em 21/07. A ausência da busca não prova venda.
- `1263868` foi retirado por “VENDIDO” na primeira foto. `1322206` também declara venda fechada na descrição.
- `1212351`, `1338088`, `1259968`, `1315328`, `1372539`, `1313649` e `1370861` têm conversão ou conflito relevante de planta.
- Os grupos `1366910`/`1372290`/`1383534`, `1370948`/`1371797`/`1372167` e `1357834`/`1358242`/`1382413` são duplicatas prováveis ou têm endereço/área divergentes.
- O ranking serve para ordenar investigação. Andar, elevador, vaga, matrícula, instalações, ruído, insolação e custo de obra exigem confirmação.
## Próxima ação
Solicitar, antes de cada visita, planta ou matrícula, andar, existência de elevador, natureza jurídica da vaga, atas recentes do condomínio e histórico de elétrica/hidráulica. As posições 13–15 são alternativas de preço: só avançam se orçamento de reforma, escadas e ausência de garagem forem compatíveis com permanência de longo prazo e preservação da reserva.
## Reprodutibilidade
Execute `python reports/cruzeiro_top15/analysis.py` para a extração somente leitura. A ordenação final inclui julgamento fotográfico documentado neste arquivo e não é produzida automaticamente pelo script.

2
reports/cruzeiro_top10/analysis.py → reports/cruzeiro_top15/analysis.py

@ -75,7 +75,7 @@ def main() -> int:
l.raw_html_path, COUNT(p.source_url) AS photo_count l.raw_html_path, COUNT(p.source_url) AS photo_count
FROM listings AS l FROM listings AS l
JOIN listing_searches AS s USING (listing_id) JOIN listing_searches AS s USING (listing_id)
LEFT JOIN photos AS p USING (listing_id) LEFT JOIN photos AS p ON p.listing_id=l.listing_id AND p.is_primary_gallery=1
WHERE instr(s.search_url, ?) > 0 WHERE instr(s.search_url, ?) > 0
AND l.inactive_at IS NULL AND l.inactive_at IS NULL
GROUP BY l.listing_id GROUP BY l.listing_id

20
reports/cruzeiro_top15/parameters.json

@ -0,0 +1,20 @@
{
"analysis_date": "2026-07-21",
"listing_data_reference_date": "2026-07-21",
"database_path": "dfimoveis_data/dfimoveis.sqlite3",
"database_main_file_sha256": "bfbbdd09e88f76afd917ebd981b848b9321aeb5fc0602f712645d33e1a5ea76c",
"database_mode": "read_only_transaction_with_wal",
"search_url_fragment": "/cruzeiro/novo/",
"listing_limit": 100,
"observed_listing_count": 67,
"active_listing_count": 63,
"inactive_listing_count": 4,
"database_crawl_run_count": 20,
"ranking_size": 15,
"ranking_unit": "probable_physical_property",
"numeric_score_used": false,
"price_per_m2_used": false,
"first_photo_finalist_audit_count": 16,
"visually_unavailable_listing_ids": ["1263868"],
"context_documents_reviewed": ["docs/CURRENT_STATE.md", "docs/PROJECT_CONTEXT.md", "docs/LOCATION_CRITERIA.md", "docs/PROPERTY_CRITERIA.md", "docs/FINANCIAL_STRATEGY.md", "docs/SCRAPER_OPERATIONS.md", "docs/DATA_MODEL.md", "docs/ANALYSIS_RULES.md"]
}

6
reports/cruzeiro_top10/photo_similarity.py → reports/cruzeiro_top15/photo_similarity.py

@ -22,7 +22,7 @@ DATA_ROOT = DB_PATH.parent
def difference_hash(path: Path) -> int | None: def difference_hash(path: Path) -> int | None:
try: try:
with Image.open(path) as image: with Image.open(path) as image:
pixels = list(image.convert("L").resize((9, 8)).getdata()) pixels = list(image.convert("L").resize((9, 8)).get_flattened_data())
except (OSError, UnidentifiedImageError): except (OSError, UnidentifiedImageError):
return None return None
value = 0 value = 0
@ -48,7 +48,7 @@ def main() -> None:
), uncommon AS ( ), uncommon AS (
SELECT sha256 SELECT sha256
FROM photos FROM photos
WHERE sha256 IS NOT NULL WHERE sha256 IS NOT NULL AND is_primary_gallery=1
GROUP BY sha256 GROUP BY sha256
HAVING COUNT(DISTINCT listing_id) <= 5 HAVING COUNT(DISTINCT listing_id) <= 5
) )
@ -56,7 +56,7 @@ def main() -> None:
FROM photos AS p FROM photos AS p
JOIN cruzeiro AS c USING (listing_id) JOIN cruzeiro AS c USING (listing_id)
JOIN uncommon AS u USING (sha256) JOIN uncommon AS u USING (sha256)
WHERE p.local_path IS NOT NULL WHERE p.local_path IS NOT NULL AND p.is_primary_gallery=1
ORDER BY p.listing_id, p.ordinal ORDER BY p.listing_id, p.ordinal
""" """
).fetchall() ).fetchall()

1
reports/cruzeiro_top10/query.sql → reports/cruzeiro_top15/query.sql

@ -26,6 +26,7 @@ SELECT p.listing_id, COUNT(*) AS photo_count
FROM photos AS p FROM photos AS p
JOIN listing_searches AS s USING (listing_id) JOIN listing_searches AS s USING (listing_id)
WHERE instr(s.search_url, '/cruzeiro/novo/') > 0 WHERE instr(s.search_url, '/cruzeiro/novo/') > 0
AND p.is_primary_gallery = 1
AND EXISTS ( AND EXISTS (
SELECT 1 FROM listings AS l SELECT 1 FROM listings AS l
WHERE l.listing_id = p.listing_id AND l.inactive_at IS NULL WHERE l.listing_id = p.listing_id AND l.inactive_at IS NULL

16
reports/cruzeiro_top15/results.csv

@ -0,0 +1,16 @@
rank,representative_listing_id,probable_duplicate_ids,price_brl,area_m2,bedrooms,suites,floor,elevator,parking_spaces,original_three_bedroom_status,visual_status,decision
1,1345146,,670000,65,3,1,4,yes,,probable_pending_confirmation,no_sale_marker,strong_candidate
2,1357415,,630000,67.2,3,1,4,unknown,,probable_pending_confirmation,no_sale_marker,confirm_elevator_then_visit
3,1357633,,600000,64,3,0,2,unknown,,probable_pending_confirmation,no_sale_marker,visit
4,1376958,,599000,62,3,1,2,no,,probable_pending_confirmation,no_sale_marker,visit
5,1345903,,565000,64.5,3,1,1,unknown,0,probable_pending_confirmation,no_sale_marker,visit_as_value_option
6,1339324,,665000,64,3,1,3,unknown,,probable_pending_confirmation,no_sale_marker,clarify_accessibility_then_visit
7,1370889,1375200,695000,62,3,1,2,unknown,,probable_pending_confirmation,no_sale_marker,identify_primary_listing_then_visit_once
8,1372268,,595000,62.61,3,0,1,unknown,2,probable_pending_confirmation,no_sale_marker,prove_parking_then_visit
9,1373131,,725000,65,3,0,2,no,,probable_pending_confirmation,no_sale_marker,visit_if_reform_premium_is_acceptable
10,1381514,,690000,63,3,0,4,unknown,,probable_pending_confirmation,no_sale_marker,confirm_elevator_parking_and_area_then_visit
11,1382466,,650000,62,3,0,,unknown,1,probable_pending_confirmation,no_sale_marker,request_missing_rooms_before_visit
12,1290510,,640000,62,3,1,4,no,,probable_pending_confirmation,no_sale_marker,validate_costs_then_visit
13,1371813,,709000,68,3,1,,unknown,,probable_pending_confirmation,no_sale_marker,request_full_gallery_then_decide
14,1400223,,495000,62.61,3,0,2,unknown,,probable_pending_confirmation,no_sale_marker,quote_full_renovation_and_confirm_parking
15,1369327,,490000,62,3,0,4,no,1,probable_pending_confirmation,no_sale_marker,quote_reform_and_test_stairs
1 rank representative_listing_id probable_duplicate_ids price_brl area_m2 bedrooms suites floor elevator parking_spaces original_three_bedroom_status visual_status decision
2 1 1345146 670000 65 3 1 4 yes probable_pending_confirmation no_sale_marker strong_candidate
3 2 1357415 630000 67.2 3 1 4 unknown probable_pending_confirmation no_sale_marker confirm_elevator_then_visit
4 3 1357633 600000 64 3 0 2 unknown probable_pending_confirmation no_sale_marker visit
5 4 1376958 599000 62 3 1 2 no probable_pending_confirmation no_sale_marker visit
6 5 1345903 565000 64.5 3 1 1 unknown 0 probable_pending_confirmation no_sale_marker visit_as_value_option
7 6 1339324 665000 64 3 1 3 unknown probable_pending_confirmation no_sale_marker clarify_accessibility_then_visit
8 7 1370889 1375200 695000 62 3 1 2 unknown probable_pending_confirmation no_sale_marker identify_primary_listing_then_visit_once
9 8 1372268 595000 62.61 3 0 1 unknown 2 probable_pending_confirmation no_sale_marker prove_parking_then_visit
10 9 1373131 725000 65 3 0 2 no probable_pending_confirmation no_sale_marker visit_if_reform_premium_is_acceptable
11 10 1381514 690000 63 3 0 4 unknown probable_pending_confirmation no_sale_marker confirm_elevator_parking_and_area_then_visit
12 11 1382466 650000 62 3 0 unknown 1 probable_pending_confirmation no_sale_marker request_missing_rooms_before_visit
13 12 1290510 640000 62 3 1 4 no probable_pending_confirmation no_sale_marker validate_costs_then_visit
14 13 1371813 709000 68 3 1 unknown probable_pending_confirmation no_sale_marker request_full_gallery_then_decide
15 14 1400223 495000 62.61 3 0 2 unknown probable_pending_confirmation no_sale_marker quote_full_renovation_and_confirm_parking
16 15 1369327 490000 62 3 0 4 no 1 probable_pending_confirmation no_sale_marker quote_reform_and_test_stairs

179
reports/cruzeiro_vs_mangueiral/README.md

@ -1,133 +1,88 @@
# Ranking reconstruído — Cruzeiro Novo x Jardins Mangueiral # Top 30 — Jardins Mangueiral x Cruzeiro Novo
**Data do snapshot:** 17/07/2026 **Snapshot:** 21/07/2026
**Unidade:** imóvel físico provável
**Finalidade:** priorizar investigação e visitas; não estimar preço de transação nem substituir vistoria
## Resumo executivo
O Jardins Mangueiral continua oferecendo os melhores candidatos por espaço, suíte, vagas e preço anunciado. O Cruzeiro Novo continua superior em centralidade, acesso ao Plano Piloto e menor dependência de carro. **Unidade:** imóvel físico provável
A casa `1279892`, QC 10, permanece em primeiro lugar geral pelo equilíbrio entre R$ 595 mil, condição visual, três quartos prováveis e quintal convertido sem eliminação total da área aberta. O apartamento `1345146`, SHCES 1207, é o melhor do Cruzeiro e fica em segundo: reforma coerente, suíte e elevador anunciado, mas com preço maior e vaga ainda incerta. **Finalidade:** ordenar investigação para moradia de 10–20 anos; não estimar preço de transação nem substituir vistoria e análise financeira.
Confiança do ranking: **moderada para priorização de visitas e baixa para decisão de compra**. Fotos e anúncios não confirmam documentação, estrutura, sistemas, ruído, conforto térmico ou preço negociável. ## Resposta
## Universo e atualização Após reaplicar o contexto do projeto, o **Jardins Mangueiral é a região principal** e o Cruzeiro Novo é alternativa secundária e oportunística. O Mangueiral tende a entregar mais espaço, suíte, vagas e ambiente residencial pelo preço; o Cruzeiro só supera essa preferência quando uma unidade concreta compensa claramente as concessões de escadas, garagem, reforma e funcionalidade pela centralidade.
- Fonte: DF Imóveis. `1279892` permanece em primeiro lugar geral. `1345146` é o melhor apartamento e ocupa o terceiro lugar, atrás também de `1350367`. A confiança é **moderada para ordenar investigação e baixa para decidir compra**, pois documentação, estrutura, ruído, conforto térmico, custos de adequação e preço negociado não estão comprovados.
- Banco: 159 anúncios, 295 snapshots e 6.272 mídias.
- Cruzeiro Novo: 65 anúncios acumulados e ativos.
- Jardins Mangueiral: 94 anúncios acumulados; 84 ativos no banco e 10 inativos.
- Cobertura útil: coletas de 13 e 17/07/2026.
- Histórico: ainda curto demais para tendência, liquidez ou tempo real de mercado.
O banco foi aberto com `mode=ro`, `PRAGMA query_only = ON` e transação de leitura consistente para incluir o WAL. O `quick_check` retornou `ok`. ## Universo, período e qualidade
## Método de reconstrução - Fonte: DF Imóveis; cobertura observada de 13–21/07/2026.
- Banco aberto com `mode=ro`, `PRAGMA query_only=ON`; `quick_check`: `ok`.
- 161 anúncios, 562 observações históricas e 7.363 fotos.
- Cruzeiro: 67 anúncios acumulados, 63 ativos e 4 inativos.
- Mangueiral: 94 acumulados, 78 ativos e 16 inativos.
- A coleta de 21/07 adicionou dois anúncios do Cruzeiro e nenhum do Mangueiral; 20 anúncios acumulados estão inativos.
- Das 401 transições históricas observadas, 398 envolvem o campo `creci` contaminado; somente três anúncios tiveram mudança real de preço. O histórico bruto não deve ser lido como 401 mudanças de mercado.
- Não há série suficiente para inferir liquidez, tendência ou venda a partir de retirada.
- Preços são de oferta; áreas não são comparáveis o bastante para preço por m².
O comparativo foi refeito a partir dos rankings regionais corrigidos, preservando a ordem relativa dentro de cada região. Não foram usados preço por metro quadrado nem pontuação numérica, porque as áreas das casas e apartamentos não são semanticamente comparáveis e os pesos não foram aprovados. ## Método
A ordem geral combina: Foram relidos `PROJECT_CONTEXT.md`, `PROPERTY_CRITERIA.md`, `DATA_MODEL.md` e `ANALYSIS_RULES.md`. A ordem combina os rankings regionais sem pesos secretos e sem alternância automática, considerando: três quartos utilizáveis, funcionalidade futura, condição aparente, iluminação/ventilação, acessibilidade, vagas, regularidade, custo de adequação, logística familiar e qualidade da evidência.
1. funcionalidade de longo prazo e três quartos utilizáveis; No Mangueiral, planta original de três quartos é filtro obrigatório; quintal fechado só é negativo quando reduz luz/ventilação ou cria ambiente redundante e de manutenção difícil. No Cruzeiro, escadas, ausência de garagem e sistemas antigos são concessões materiais para permanência de longo prazo. A proximidade de São Sebastião é benefício logístico, não fundamento exclusivo da decisão.
2. condição visual e risco de reforma;
3. iluminação, ventilação, circulação e armazenamento aparentes;
4. preço, custos conhecidos e margem para adequação;
5. acessibilidade, vagas e dependência de carro;
6. riscos específicos da tipologia;
7. qualidade e coerência da evidência fotográfica;
8. compatibilidade da localização com a rotina registrada no projeto.
No Cruzeiro, os principais ajustes de risco são escadas, ausência de garagem, sistemas antigos e obras do bloco. No Mangueiral, são planta original, regularização das ampliações, impermeabilização, calor/ventilação e logística. A ordem regional do Mangueiral foi recalibrada após o usuário avaliar diretamente os 15 finalistas em 21/07 e, em seguida, o funil foi refeito sobre os 78 anúncios ativos. As notas pessoais prevalecem quando existem; anúncios novos entram por aderência inferida e ficam explicitamente pendentes de avaliação pessoal. Para não inventar equivalência numérica com apartamentos ainda não avaliados, as posições relativas do Cruzeiro foram mantidas.
## Auditoria de disponibilidade pelas fotos Republicações visualmente equivalentes contam como um imóvel provável. Falta de informação é pendência, não aprovação.
A primeira foto dos 20 finalistas foi inspecionada especificamente para “vendido”, “negócio fechado” ou sinal equivalente. ## Auditoria visual de disponibilidade
- Nenhum dos dez finalistas do Cruzeiro apresentou esse sinal. A primeira foto corrente dos 78 anúncios ativos do Mangueiral, dos finalistas do Cruzeiro e das quarentenas foi aberta e inspecionada. Nenhum dos 30 imóveis classificados atuais exibe “vendido”, “negócio fechado” ou equivalente. Ficam em quarentena `1263868` no Cruzeiro e `1301400`, `1346017`, `1346231`, `1349519`, `1351729`, `1352943`, `1356197`, `1362968` e `1366801` no Mangueiral. O sinal visual indica provável indisponibilidade operacional, não prova jurídica de venda.
- No Mangueiral, quatro anúncios que apareceram em versões anteriores foram removidos e colocados em quarentena visual: `1346231`, `1349519`, `1351729` e `1366801`.
- O banco ainda os tratava como ativos; para um ranking de visitas, a evidência visual prevaleceu como sinal de indisponibilidade operacional.
- A marca não comprova registro, preço ou conclusão jurídica da transação.
## Ranking geral ## Ranking geral
| # | Região | Anúncio | Local | Preço | Motivo principal | Pendência decisiva | | # | Região | Pos. regional | Anúncio | Local | Preço | Classificação e pendência decisiva |
|---:|---|---|---|---:|---|---| |---:|---|---:|---|---|---:|---|
| 1 | Mangueiral | [1279892](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-rua-f-1279892) | QC 10 | R$ 595.000 | Melhor equilíbrio entre preço, estado e espaço externo | Planta, áreas divergentes e regularização | | 1 | Mangueiral | 1 | [1279892](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-rua-f-1279892) | QC 10 | R$ 595 mil | **Forte:** equilíbrio geral; confirmar planta, áreas e regularização |
| 2 | Cruzeiro | [1345146](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1207-bloco-a-1345146) | SHCES 1207 A | R$ 670.000 | Melhor alternativa urbana; reformado e com elevador anunciado | Vaga, área da matrícula e sistemas | | 2 | Mangueiral | 2 | [1319996](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1319996) | QC 14 | R$ 649 mil | **Forte, 4/5:** cozinha, gourmet, quintal e conservação; reverter closet se necessário |
| 3 | Mangueiral | [1350367](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-6-rua-c-1350367) | QC 6 | R$ 569.000 | Preço competitivo e ampliação social aparentemente funcional | Área, licenças, calor e drenagem | | 3 | Cruzeiro | 1 | [1345146](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1207-bloco-a-1345146) | SHCES 1207 A | R$ 670 mil | **Forte:** melhor opção urbana; confirmar vaga, planta e sistemas |
| 4 | Cruzeiro | [1232283](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-309-bloco-b-1232283) | SHCES 309 B | R$ 600.000 | 70 m², suíte, nascente/vazado e duas vagas anunciadas | Elevador e natureza jurídica das vagas | | 4 | Mangueiral | 3 | [1377683](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1377683) | QC 11 | R$ 585 mil | **Aderência inferida:** cozinha funcional e gourmet aberto; usuário avaliar |
| 5 | Mangueiral | [1346017](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1346017) | QC 10 | R$ 577.000 | Clara, pouco personalizada e com quintal majoritariamente aberto | Taxas, orientação e regularização da cobertura | | 5 | Mangueiral | 4 | [1371718](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-rua-c-1371718) | QC 14 | R$ 650 mil | **Aderência inferida:** conservação, cozinha e gourmet; usuário avaliar |
| 6 | Mangueiral | [1397378](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-8-rua-b-1397378) | QC 8 | R$ 600.000 | QC prática e implantação compatível com a planta de 68 m² | Manutenção, drenagem e galeria contaminada | | 6 | Cruzeiro | 2 | [1357415](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-1357415) | SHCES 1109 | R$ 630 mil | **Forte condicionado:** reformado e vazado; confirmar elevador |
| 7 | Cruzeiro | [1357415](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-1357415) | SHCES 1109 | R$ 630.000 | Boa reforma, suíte e posição nascente/vazada | Confirmar elevador antes de agendar | | 7 | Mangueiral | 5 | [1140317](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-5-rua-l-1140317) | QC 5 | R$ 599 mil | **Aderência inferida:** cozinha equipada e gourmet aberto; usuário avaliar |
| 8 | Mangueiral | [1359960](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-7-1359960) | QC 7 | R$ 610.000 | Ampliação social flexível sem segunda cozinha completa evidente | Calor, ventilação e impermeabilização | | 8 | Mangueiral | 6 | [1324014](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-12-rua-k-1324014) | QC 12 | R$ 649 mil | **Visitar, 3/5:** gourmet e conservação agradam; confirmar vaga e ventilação |
| 9 | Cruzeiro | [1357633](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-309-1357633) | SHCES 309 | R$ 600.000 | Reforma uniforme e sistemas declarados renovados | Elevador, garagem, condomínio e prova da reforma | | 9 | Cruzeiro | 3 | [1357633](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-309-1357633) | SHCES 309 | R$ 600 mil | **Visitar:** reforma uniforme; elevador, vaga e sistemas pendentes |
| 10 | Cruzeiro | [1376958](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-b-1376958) | SHCES 911 B | R$ 599.000 | Reformado, nascente, vazado e com suíte | Sem elevador e garagem não informada | | 10 | Mangueiral | 7 | [1290502](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1290502) | QC 11 | R$ 600 mil | **Aprofundar, 3/5:** reforma adequada; gourmet implementável |
| 11 | Mangueiral | [1319996](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1319996) | QC 14 | R$ 649.000 | Reforma completa, marcenaria e placas solares | QC, cobertura, taxas e homologação solar | | 11 | Cruzeiro | 4 | [1376958](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-b-1376958) | SHCES 911 B | R$ 599 mil | **Visitar:** reformado e nascente; sem elevador e vaga incerta |
| 12 | Cruzeiro | [1345903](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1303-1345903) | SHCES 1303 | R$ 565.000 | Menor preço competitivo e primeiro andar | Sem garagem; ruído e segurança do térreo | | 12 | Mangueiral | 8 | [1352633](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-9-rua-g-1352633) | QC 9 | R$ 755 mil | **Aderência inferida:** conjunto visual forte; preço alto e usuário avaliar |
| 13 | Mangueiral | [1324014](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-12-rua-k-1324014) | QC 12 | R$ 649.000 | Acabamento interno e marcenaria fortes | Vaga, redundância gourmet e ventilação | | 13 | Mangueiral | 9 | [1345745](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-quadra-qc-12-1345745) | QC 12 | R$ 670 mil | **Aderência inferida:** cozinha e gourmet amplos; testar calor |
| 14 | Cruzeiro | [1339324](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-703-1339324) | SHCES 703 | R$ 665.000 | Integração social, suíte e boa apresentação visual | Terceiro andar, elevador e garagem desconhecidos | | 14 | Cruzeiro | 5 | [1345903](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1303-1345903) | SHCES 1303 | R$ 565 mil | **Alternativa de valor:** primeiro andar, mas sem garagem |
| 15 | Mangueiral | [1333569](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-6-1333569) | QC 6 | R$ 650.000 | Ampliação social em uso e QC alternativa prática | Conceito dos 90 m² e manutenção da cobertura | | 15 | Mangueiral | 10 | [1333569](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-6-1333569) | QC 6 | R$ 650 mil | **Condicionado, 3/5:** testar calor do fechamento de vidro |
| 16 | Cruzeiro | [1370889](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1370889) / [1375200](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1375200) | SHCES 207 | R$ 695.000 | Apartamento reformado com suíte | Duplicata, andar, elevador, vaga e área | | 16 | Cruzeiro | 6 | [1339324](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-703-1339324) | SHCES 703 | R$ 665 mil | **Aprofundar:** boa integração; faltam andar, elevador e vaga |
| 17 | Mangueiral | [1359480](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-4-1359480) | QC 4 | R$ 535.000 | Referência de menor preço habitável | QC, planta, documentação e acabamento externo | | 17 | Mangueiral | 11 | [1357311](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-1-rua-a-1357311) | QC 1 | R$ 699,9 mil | **Condicionado:** gourmet bom, mas há possível dano de água no teto |
| 18 | Cruzeiro | [1372268](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-bloco-i-1372268) | SHCES 305 I | R$ 595.000 | Primeiro andar e duas vagas anunciadas | Provar vagas e orçar modernização | | 18 | Mangueiral | 12 | [1357912](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-3-1357912) | QC 3 | R$ 580 mil | **Condicionado, 3/5:** espaços úteis; intervenções pesam |
| 19 | Mangueiral | [1356197](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-4-1356197) | QC 4 | R$ 649.000 | Acabamento interno forte e casa vazia | Espaço coberto parece garagem; regularização | | 19 | Cruzeiro | 7 | [1370889](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1370889) / [1375200](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-207-1375200) | SHCES 207 | R$ 695 mil | **Aprofundar:** reformado; identificar anúncio principal e atributos |
| 20 | Cruzeiro | [1373131](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-1373131) | SHCES 305 | R$ 725.000 | Reforma tecnicamente bem descrita | Sem elevador, sem garagem e prêmio de preço | | 20 | Cruzeiro | 8 | [1372268](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-bloco-i-1372268) | SHCES 305 I | R$ 595 mil | **Alternativa de valor:** provar duas vagas e orçar modernização |
| 21 | Mangueiral | 13 | [1259149](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1259149) | QC 11 | R$ 590 mil | **Aderência inferida fraca:** preço razoável; acabamento e área pendentes |
## Comparação regional | 22 | Cruzeiro | 9 | [1373131](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-305-1373131) | SHCES 305 | R$ 725 mil | **Condicionado:** reforma forte; sem elevador/garagem e caro |
| 23 | Mangueiral | 14 | [348115](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-rua-c-348115) | QC 14 | R$ 550 mil | **Referência de valor:** anúncio antigo; conservação e área pendentes |
| Dimensão | Cruzeiro Novo | Jardins Mangueiral | | 24 | Cruzeiro | 10 | [1381514](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-301-1381514) | SHCES 301 | R$ 690 mil | **Condicionado:** reforma forte; 4º andar, elevador/vaga e área divergente pendentes |
|---|---|---| | 25 | Mangueiral | 15 | [1346118](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1346118) | QC 14 | R$ 550 mil | **Referência de valor:** base simples; modernização e área divergente |
| Faixa do top 10 | R$ 565 mil–R$ 725 mil | R$ 535 mil–R$ 650 mil | | 26 | Cruzeiro | 11 | [1382466](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-101-bloco-c-1382466) | SHCES 101 C | R$ 650 mil | **Pendente:** boa reforma; galeria curta e andar/elevador ausentes |
| Mediana do top 10 | R$ 615 mil | R$ 610 mil | | 27 | Cruzeiro | 12 | [1290510](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-a-1290510) | SHCES 911 A | R$ 640 mil | **Pendente:** suíte e reforma; 4º andar e taxas inválidas |
| Espaço | 62–70 m²; conceito de área mais homogêneo | 68–90 m² informados; ampliações pouco comparáveis | | 28 | Cruzeiro | 13 | [1371813](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-bloco-d-1371813) | SHCES 1109 D | R$ 709 mil | **Pendente:** reforma moderna; evidência curta da planta |
| Vagas | Frequentemente ausentes ou incertas | Uma ou duas na maioria dos finalistas | | 29 | Cruzeiro | 14 | [1400223](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-609-bloco-d-1400223) | SHCES 609 D | R$ 495 mil | **Alternativa de obra:** 2º andar e boa luz; reforma ampla e vaga incerta |
| Acessibilidade | Depende fortemente de andar e elevador | Escada interna permanente em todos os sobrados padrão | | 30 | Cruzeiro | 15 | [1369327](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-203-bloco-g-1369327) | SHCES 203 G | R$ 490 mil | **Alternativa de obra:** vaga e desconto; 4º andar sem elevador |
| Reforma | Elétrica, hidráulica e prédio antigos | Cobertura, drenagem, impermeabilização e ampliação |
| Mobilidade | Centralidade e menor dependência de carro | Maior dependência logística; proximidade de São Sebastião | ## Quase entraram
| Espaço social | Compacto | Quintal e ampliações mais flexíveis |
| Documentação crítica | Vaga, área, obras do bloco | Planta original e regularização das ampliações | As opções seguintes, todas com URL, estão nos relatórios regionais: [Cruzeiro Novo](../cruzeiro_top15/README.md#quase-entraram) e [Jardins Mangueiral](../mangueiral_top15/README.md#quase-entraram).
## Sensibilidade à prioridade pessoal ## Próxima ação e decisão financeira
| Prioridade dominante | Região favorecida | Primeiro candidato | No Mangueiral, começar a diligência por `1279892` e `1319996`; pedir planta, QC, licenças da ampliação, taxas e evidência de cobertura/drenagem antes da visita. No Cruzeiro, pedir andar, elevador, matrícula da vaga, atas e histórico de sistemas do prédio. As posições gerais entre tipologias permanecem provisórias até o usuário avaliar visualmente os apartamentos na mesma escala.
|---|---|---|
| Centralidade e acesso ao Plano | Cruzeiro | 1345146 | O ranking físico não autoriza compra. Antes de negociar, calcular entrada, custos de aquisição, reforma/regularização, reserva remanescente, CET e custo mensal total. A posição deve cair se o imóvel exigir consumir a reserva ou depender de uma única circunstância afetiva/logística.
| Espaço, suíte, vagas e custo-benefício | Mangueiral | 1279892 |
| Menor preço habitável | Mangueiral | 1359480, com concessões |
| Apartamento próximo de R$ 600 mil | Cruzeiro | 1232283 |
| Menor risco de ampliação irregular | Cruzeiro | 1345146 ou 1357633 |
| Menor risco de sistemas prediais antigos | Mangueiral | 1346017 ou 1397378, sujeito a vistoria |
| Proximidade de São Sebastião e rede familiar | Mangueiral | 1279892 |
## Imóveis imediatamente fora do corte
- **Cruzeiro:** [1382466](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-101-bloco-c-1382466), [1290510](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-911-bloco-a-1290510) e [1371813](https://www.dfimoveis.com.br/imovel/apartamento-3-quartos-venda-novo-cruzeiro-df-shces-quadra-1109-bloco-d-1371813).
- **Mangueiral:** [1368562](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-5-1368562).
## Próxima ação recomendada
As quatro visitas mais informativas continuam sendo:
1. `1279892`, para testar a melhor hipótese de casa equilibrada;
2. `1345146`, para testar a melhor hipótese urbana com elevador;
3. `1350367`, para testar custo-benefício e ampliação no Mangueiral;
4. `1232283`, para testar apartamento maior, suíte e vagas anunciadas.
Registrar após cada visita: tempo real de deslocamento, sensação de espaço, ruído, estacionamento, escadas, ventilação, manutenção esperada e vontade de permanecer por 10–20 anos. Essas quatro visitas devem definir qual região recebe a rodada seguinte.
## Limitações
- Preços são pedidos, não transações.
- A cobertura histórica é curta e de um único portal.
- A situação financeira do projeto precisa ser confirmada antes de proposta ou preço máximo.
- Fotos não validam estrutura, sistemas, ruído, conforto térmico ou documentação.
- A planta original das casas e vagas/elevadores dos apartamentos continuam pendentes.
- A escolha regional ainda depende de uma preferência não consolidada: centralidade versus casa, espaço e proximidade familiar.
## Reprodutibilidade ## Reprodutibilidade
- `results.csv`: ranking reconstruído. As extrações estão em `reports/cruzeiro_top15/analysis.py` e `reports/mangueiral_top15/analysis.py`. Os arquivos `results.csv` registram os 15 classificados regionais e `parameters.json` registra snapshot, contagens, hash do banco e critérios. A deduplicação e a inspeção fotográfica incluem julgamento manual documentado.
- `parameters.json`: universo, método e fingerprint lógico do snapshot.
- `reports/cruzeiro_top10/`: fontes do ranking regional do Cruzeiro.
- `reports/mangueiral_top10/`: fontes do ranking regional do Mangueiral.

46
reports/cruzeiro_vs_mangueiral/parameters.json

@ -1,31 +1,33 @@
{ {
"analysis_date": "2026-07-17", "analysis_date": "2026-07-21",
"listing_data_reference_date": "2026-07-17", "listing_data_reference_date": "2026-07-21",
"database_path": "dfimoveis_data/dfimoveis.sqlite3", "database_path": "dfimoveis_data/dfimoveis.sqlite3",
"database_main_file_sha256": "046e0a2fef3eb3c2e5ebdf07d30bd04a31db0bb1ea315ccc536c6bf8ba96577e", "database_main_file_sha256": "bfbbdd09e88f76afd917ebd981b848b9321aeb5fc0602f712645d33e1a5ea76c",
"logical_snapshot_sha256": "fe604aab8af7f67c833e6de2a0fc81c7dc2950ca440ad7b23ebd03d0c2546188",
"database_mode": "read_only_transaction_with_wal", "database_mode": "read_only_transaction_with_wal",
"source_reports": [ "source_reports": [
"reports/cruzeiro_top10/README.md", "reports/cruzeiro_top15/README.md",
"reports/mangueiral_top10/README.md" "reports/mangueiral_top15/README.md"
], ],
"cruzeiro_listing_count": 65, "total_listing_count": 161,
"mangueiral_listing_count": 94, "database_snapshot_count": 562,
"mangueiral_active_listing_count": 84, "database_photo_count": 7363,
"mangueiral_inactive_listing_count": 10, "database_crawl_run_count": 20,
"total_listing_count": 159, "cruzeiro_observed_listing_count": 67,
"database_snapshot_count": 295, "cruzeiro_active_listing_count": 63,
"database_photo_count": 6272, "mangueiral_observed_listing_count": 94,
"database_crawl_run_count": 16, "mangueiral_active_listing_count": 78,
"cruzeiro_photo_files_reviewed_distinct": 1289, "regional_ranking_size": 15,
"mangueiral_photo_files_reviewed_distinct_cumulative": 1907, "regional_finalists_compared": 30,
"total_photo_files_reviewed_distinct_cumulative": 3196,
"regional_finalists_compared": 20,
"ranking_merge_method": "preserve_regional_order_then_interleave_by_cross_typology_visit_priority",
"report_rebuilt_from_regional_results": true,
"ranking_unit": "probable_physical_property", "ranking_unit": "probable_physical_property",
"ranking_merge_method": "contextual_cross_typology_priority_with_mangueiral_as_primary_region_and_regional_order_preserved",
"numeric_score_used": false, "numeric_score_used": false,
"mangueiral_user_feedback_date": "2026-07-21",
"mangueiral_user_visual_functional_scores_used": true,
"mangueiral_full_active_universe_reprocessed": true,
"cross_typology_price_per_m2_used": false, "cross_typology_price_per_m2_used": false,
"first_photo_sale_marker_audit_count": 20, "mangueiral_first_photo_active_universe_audit_count": 78,
"visually_unavailable_listing_ids": ["1346231", "1349519", "1351729", "1366801"] "cruzeiro_first_photo_finalist_and_quarantine_audit_count": 16,
"visually_unavailable_listing_ids": ["1263868", "1301400", "1346017", "1346231", "1349519", "1351729", "1352943", "1356197", "1362968", "1366801"],
"source_mismatch_listing_ids": ["1369473"],
"context_documents_reviewed": ["docs/CURRENT_STATE.md", "docs/PROJECT_CONTEXT.md", "docs/LOCATION_CRITERIA.md", "docs/PROPERTY_CRITERIA.md", "docs/FINANCIAL_STRATEGY.md", "docs/SCRAPER_OPERATIONS.md", "docs/DATA_MODEL.md", "docs/ANALYSIS_RULES.md"]
} }

52
reports/cruzeiro_vs_mangueiral/results.csv

@ -1,21 +1,31 @@
rank,region,representative_listing_id,probable_duplicate_ids,location,price_brl,area_m2,bedrooms,suites,parking_spaces,key_condition,decision rank,region,regional_rank,representative_listing_id,probable_duplicate_ids,location,price_brl,decision
1,mangueiral,1279892,,QC 10,595000,68,3,1,1,planta original e ampliações pendentes de documentos,candidato forte 1,mangueiral,1,1279892,,QC 10,595000,strong_candidate
2,cruzeiro,1345146,,SHCES 1207 bloco A,670000,65,3,1,,elevador anunciado e vaga pendente,candidato forte 2,mangueiral,2,1319996,,QC 14,649000,strong_candidate
3,mangueiral,1350367,,QC 6,569000,68,3,1,2,área ampliação e conforto térmico pendentes,candidato forte 3,cruzeiro,1,1345146,,SHCES 1207 A,670000,strong_candidate
4,cruzeiro,1232283,,SHCES 309 bloco B,600000,70,3,1,2,elevador e natureza das vagas pendentes,candidato forte condicionado 4,mangueiral,3,1377683,,QC 11,585000,user_review_then_documents
5,mangueiral,1346017,,QC 10,577000,68,3,1,2,taxas orientação e cobertura pendentes,candidato forte 5,mangueiral,4,1371718,,QC 14,650000,user_review_then_documents
6,mangueiral,1397378,,QC 8,600000,68,3,1,2,planta manutenção e cobertura pendentes,candidato forte 6,cruzeiro,2,1357415,,SHCES 1109,630000,strong_candidate_conditioned
7,cruzeiro,1357415,,SHCES 1109,630000,67.2,3,1,,quarto andar e elevador desconhecido,confirmar elevador antes da visita 7,mangueiral,5,1140317,,QC 5,599000,user_review_then_documents
8,mangueiral,1359960,,QC 7,610000,68,3,1,2,conforto ambiental do quintal fechado pendente,visitar 8,mangueiral,6,1324014,,QC 12,649000,visit
9,cruzeiro,1357633,,SHCES 309,600000,64,3,0,,elevador garagem e condomínio pendentes,visitar 9,cruzeiro,3,1357633,,SHCES 309,600000,visit
10,cruzeiro,1376958,,SHCES 911 bloco B,599000,62,3,1,,sem elevador e garagem não informada,visitar 10,mangueiral,7,1290502,,QC 11,600000,investigate
11,mangueiral,1319996,,QC 14,649000,68,3,1,2,localização cobertura e placas solares pendentes,visitar 11,cruzeiro,4,1376958,,SHCES 911 B,599000,visit
12,cruzeiro,1345903,,SHCES 1303,565000,64.5,3,1,0,primeiro andar e somente estacionamento público,alternativa de valor 12,mangueiral,8,1352633,,QC 9,755000,user_review_and_price_test
13,mangueiral,1324014,,QC 12,649000,68,3,1,,vaga conforto da área gourmet e regularização pendentes,visitar 13,mangueiral,9,1345745,,QC 12,670000,user_review_and_heat_test
14,cruzeiro,1339324,,SHCES 703,665000,64,3,1,,terceiro andar elevador e garagem desconhecidos,esclarecer acessibilidade 14,cruzeiro,5,1345903,,SHCES 1303,565000,value_alternative
15,mangueiral,1333569,,QC 6,650000,90,3,1,2,conceito de área e cobertura pendentes,visitar após os anteriores 15,mangueiral,10,1333569,,QC 6,650000,conditioned_heat_test
16,cruzeiro,1370889,1375200,SHCES 207,695000,62,3,1,,duplicata provável elevador e garagem desconhecidos,confirmar anúncio primário 16,cruzeiro,6,1339324,,SHCES 703,665000,investigate
17,mangueiral,1359480,,QC 4,535000,70,3,1,2,planta documentação e acabamento externo pendentes,referência de valor 17,mangueiral,11,1357311,,QC 1,699900,inspect_possible_water_damage
18,cruzeiro,1372268,,SHCES 305 bloco I,595000,62.61,3,0,2,vagas e custo de modernização pendentes,alternativa de valor 18,mangueiral,12,1357912,,QC 3,580000,pending_intervention_cost
19,mangueiral,1356197,,QC 4,649000,68,3,1,2,uso do espaço coberto e regularização pendentes,visitar após os anteriores 19,cruzeiro,7,1370889,1375200,SHCES 207,695000,investigate
20,cruzeiro,1373131,,SHCES 305,725000,65,3,0,,sem elevador e garagem não informada,visitar apenas se reforma justificar prêmio 20,cruzeiro,8,1372268,,SHCES 305 I,595000,value_alternative
21,mangueiral,13,1259149,,QC 11,590000,user_review_and_area_validation
22,cruzeiro,9,1373131,,SHCES 305,725000,conditioned
23,mangueiral,14,348115,,QC 14,550000,user_review_and_condition_validation
24,cruzeiro,10,1381514,,SHCES 301,690000,conditioned
25,mangueiral,15,1346118,,QC 14,550000,user_review_and_renovation_scope
26,cruzeiro,11,1382466,,SHCES 101 C,650000,pending_evidence
27,cruzeiro,12,1290510,,SHCES 911 A,640000,pending_cost_validation
28,cruzeiro,13,1371813,,SHCES 1109 D,709000,pending_evidence
29,cruzeiro,14,1400223,,SHCES 609 D,495000,renovation_alternative
30,cruzeiro,15,1369327,,SHCES 203 G,490000,renovation_alternative

1 rank region regional_rank representative_listing_id probable_duplicate_ids location area_m2 price_brl bedrooms decision suites parking_spaces key_condition
2 1 mangueiral 1 1279892 QC 10 68 595000 3 candidato forte strong_candidate 1 1 planta original e ampliações pendentes de documentos
3 2 cruzeiro mangueiral 2 1345146 1319996 SHCES 1207 bloco A QC 14 65 670000 649000 3 candidato forte strong_candidate 1 elevador anunciado e vaga pendente
4 3 mangueiral cruzeiro 1 1350367 1345146 QC 6 SHCES 1207 A 68 569000 670000 3 candidato forte strong_candidate 1 2 área ampliação e conforto térmico pendentes
5 4 cruzeiro mangueiral 3 1232283 1377683 SHCES 309 bloco B QC 11 70 600000 585000 3 candidato forte condicionado user_review_then_documents 1 2 elevador e natureza das vagas pendentes
6 5 mangueiral 4 1346017 1371718 QC 10 QC 14 68 577000 650000 3 candidato forte user_review_then_documents 1 2 taxas orientação e cobertura pendentes
7 6 mangueiral cruzeiro 2 1397378 1357415 QC 8 SHCES 1109 68 600000 630000 3 candidato forte strong_candidate_conditioned 1 2 planta manutenção e cobertura pendentes
8 7 cruzeiro mangueiral 5 1357415 1140317 SHCES 1109 QC 5 67.2 630000 599000 3 confirmar elevador antes da visita user_review_then_documents 1 quarto andar e elevador desconhecido
9 8 mangueiral 6 1359960 1324014 QC 7 QC 12 68 610000 649000 3 visitar visit 1 2 conforto ambiental do quintal fechado pendente
10 9 cruzeiro 3 1357633 SHCES 309 64 600000 3 visitar visit 0 elevador garagem e condomínio pendentes
11 10 cruzeiro mangueiral 7 1376958 1290502 SHCES 911 bloco B QC 11 62 599000 600000 3 visitar investigate 1 sem elevador e garagem não informada
12 11 mangueiral cruzeiro 4 1319996 1376958 QC 14 SHCES 911 B 68 649000 599000 3 visitar visit 1 2 localização cobertura e placas solares pendentes
13 12 cruzeiro mangueiral 8 1345903 1352633 SHCES 1303 QC 9 64.5 565000 755000 3 alternativa de valor user_review_and_price_test 1 0 primeiro andar e somente estacionamento público
14 13 mangueiral 9 1324014 1345745 QC 12 68 649000 670000 3 visitar user_review_and_heat_test 1 vaga conforto da área gourmet e regularização pendentes
15 14 cruzeiro 5 1339324 1345903 SHCES 703 SHCES 1303 64 665000 565000 3 esclarecer acessibilidade value_alternative 1 terceiro andar elevador e garagem desconhecidos
16 15 mangueiral 10 1333569 QC 6 90 650000 3 visitar após os anteriores conditioned_heat_test 1 2 conceito de área e cobertura pendentes
17 16 cruzeiro 6 1370889 1339324 1375200 SHCES 207 SHCES 703 62 695000 665000 3 confirmar anúncio primário investigate 1 duplicata provável elevador e garagem desconhecidos
18 17 mangueiral 11 1359480 1357311 QC 4 QC 1 70 535000 699900 3 referência de valor inspect_possible_water_damage 1 2 planta documentação e acabamento externo pendentes
19 18 cruzeiro mangueiral 12 1372268 1357912 SHCES 305 bloco I QC 3 62.61 595000 580000 3 alternativa de valor pending_intervention_cost 0 2 vagas e custo de modernização pendentes
20 19 mangueiral cruzeiro 7 1356197 1370889 1375200 QC 4 SHCES 207 68 649000 695000 3 visitar após os anteriores investigate 1 2 uso do espaço coberto e regularização pendentes
21 20 cruzeiro 8 1373131 1372268 SHCES 305 SHCES 305 I 65 725000 595000 3 visitar apenas se reforma justificar prêmio value_alternative 0 sem elevador e garagem não informada
22 21 mangueiral 13 1259149 QC 11 590000 user_review_and_area_validation
23 22 cruzeiro 9 1373131 SHCES 305 725000 conditioned
24 23 mangueiral 14 348115 QC 14 550000 user_review_and_condition_validation
25 24 cruzeiro 10 1381514 SHCES 301 690000 conditioned
26 25 mangueiral 15 1346118 QC 14 550000 user_review_and_renovation_scope
27 26 cruzeiro 11 1382466 SHCES 101 C 650000 pending_evidence
28 27 cruzeiro 12 1290510 SHCES 911 A 640000 pending_cost_validation
29 28 cruzeiro 13 1371813 SHCES 1109 D 709000 pending_evidence
30 29 cruzeiro 14 1400223 SHCES 609 D 495000 renovation_alternative
31 30 cruzeiro 15 1369327 SHCES 203 G 490000 renovation_alternative

358
reports/database_assessment.md

@ -1,303 +1,169 @@
# Avaliação inicial do banco de dados # Avaliação do banco de dados
**Data da avaliação:** 13 de julho de 2026 **Data da avaliação:** 21 de julho de 2026
**Banco:** `dfimoveis_data/dfimoveis.sqlite3`
**Escopo:** diagnóstico estrutural e de qualidade anterior a qualquer análise dos imóveis
## 1. Pergunta respondida **Banco:** `dfimoveis_data/dfimoveis.sqlite3`
O banco já representa de forma segura anúncios, snapshots históricos e imóveis físicos, com cobertura e qualidade suficientes para iniciar análises imobiliárias? **Modo:** `mode=ro`, `PRAGMA query_only = ON`
**SHA-256 do arquivo principal:** `bfbbdd09e88f76afd917ebd981b848b9321aeb5fc0602f712645d33e1a5ea76c`
**Resposta curta:** ele representa anúncios e estados de conteúdo, mas ainda não representa imóveis físicos deduplicados. A coleta cobre apenas um dia e contém exatamente um snapshot por anúncio. Preços, áreas e quartos podem ser usados somente após validações específicas; localização e dados de anunciante estão fortemente contaminados e não devem ser usados como estão. Portanto, o banco ainda não sustenta tendência histórica, tempo de mercado, preço por m² ou comparáveis por localização. ## 1. Pergunta
## 2. Segurança e método O snapshot atualizado sustenta a revisão dos rankings e já permite conclusões de histórico, disponibilidade e liquidez?
A análise foi feita exclusivamente com consultas agregadas ou amostras pequenas. O banco foi aberto com `sqlite3 -readonly` ou URI `mode=ro&immutable=1`, sempre com `PRAGMA query_only = ON`. Nenhum scraper foi executado e nenhuma instrução SQL de escrita foi enviada ao banco. **Resposta curta:** sustenta a revisão operacional dos rankings, mas ainda não sustenta inferência de liquidez ou preço de transação. O banco contém quatro dias efetivos de observação entre 13 e 21/07/2026, e a maior parte dos novos snapshots é causada por campos textuais contaminados, não por mudança imobiliária real.
Identificação do arquivo analisado: ## 2. Integridade e esquema
- tamanho: 5.017.600 bytes; - tamanho do arquivo principal: 9.478.144 bytes;
- modificação: `2026-07-13 14:20:34.601285975 -03:00`;
- SHA-256 inicial: `1ce3128b488c50c993d7328ac6e24b5905f083a7e04e3f636407eea1edd02b9d`;
- sem arquivos `-wal` ou `-shm` e sem processo com o arquivo aberto na inspeção inicial;
- `PRAGMA quick_check`: `ok`; - `PRAGMA quick_check`: `ok`;
- `PRAGMA foreign_key_check`: nenhuma violação. - `PRAGMA foreign_key_check`: nenhuma violação;
- codificação: UTF-8;
Na validação final, o arquivo principal continuava com o mesmo tamanho, `mtime` e SHA-256. Como seu modo persistido é WAL, o cliente SQLite havia criado dois sidecars de runtime: `dfimoveis.sqlite3-shm`, com 32.768 bytes, e `dfimoveis.sqlite3-wal`, vazio. Eles não foram removidos, conforme a regra explícita do projeto. - `journal_mode`: WAL;
- 2.314 páginas de 4.096 bytes;
- cinco tabelas da aplicação e `sqlite_sequence`;
- quatro autoíndices; nenhuma view ou trigger;
- esquema físico sem mudança material em relação a `docs/ACTUAL_SCHEMA.md`.
O esquema completo e as decisões de interpretação estão em `docs/ACTUAL_SCHEMA.md`. O banco foi consultado em transação somente leitura. Não foi usado `immutable=1`, pois este é o arquivo atualizado pelo scraper.
## 3. Universo, período e cobertura ## 3. Universo e cobertura
Não houve filtro ou exclusão de registros para este diagnóstico. O universo físico contém: | Unidade | Total |
| Unidade | Quantidade |
|---|---:| |---|---:|
| Execuções do coletor | 4 | | Execuções registradas | 20 |
| Anúncios únicos por `listing_id` | 156 | | Execuções `ok` | 12 |
| URLs de anúncio únicas | 156 | | Execuções `failed` | 8 |
| Snapshots de conteúdo | 156 | | Anúncios acumulados | 161 |
| Vínculos anúncio–busca | 156 | | Observações históricas | 562 |
| Fotos registradas | 5.386 | | Vínculos anúncio–busca | 161 |
| Anúncios com pelo menos uma foto | 156 | | Fotos registradas | 7.363 |
| Hashes de foto distintos | 3.455 |
### Cobertura temporal | Anúncios inativos | 20 |
Todas as datas técnicas válidas estão em ISO 8601. A coleta cobre somente **13/07/2026**:
| Evento | UTC | Horário de Brasília (UTC−3) |
|---|---|---|
| Primeira execução iniciada | 12:20:58 | 09:20:58 |
| Primeira observação de anúncio | 12:56:32 | 09:56:32 |
| Última observação de anúncio | 13:25:51 | 10:25:51 |
| Última execução concluída | 17:20:34 | 14:20:34 |
As duas primeiras execuções terminaram com `status = ok`, mas registraram zero páginas, URLs e detalhes. As duas execuções com coleta efetiva somaram:
- 9 páginas de busca;
- 156 URLs encontradas;
- 156 detalhes processados com sucesso e zero falhas;
- 5.383 fotos baixadas e 3 falhas;
- zero anúncios marcados como inativos.
As buscas cobertas foram:
1. `https://www.dfimoveis.com.br/venda/df/cruzeiro/novo/apartamento/3-quartos` — 65 anúncios;
2. `https://www.dfimoveis.com.br/venda/df/brasilia/jardins-mangueiral/casa/3,4-quartos` — 91 anúncios.
O universo é, portanto, intencionalmente limitado a um portal e a duas consultas. A busca do Jardins Mangueiral aceita três **ou quatro** quartos: 89 casas foram extraídas com três quartos anunciados e 2 com quatro. Mesmo os 89 registros de três quartos não comprovam a planta original exigida pelo projeto.
## 4. Como as entidades são representadas
### Anúncio
`listings` guarda uma linha por `listing_id` do DF Imóveis, com URL única e o estado corrente. Os 156 IDs, URLs e hashes correntes são distintos. Essa é uma identidade de publicação no portal, não de unidade residencial.
### Snapshot ou observação histórica
`listing_history` guarda uma linha por par único `(listing_id, content_hash)`. Isso é um histórico de **mudanças de conteúdo**: uma nova observação idêntica não cria novo snapshot. `listings.first_seen_at` e `last_seen_at` condensam a janela observada.
No snapshot atual:
- todos os 156 anúncios têm exatamente um registro histórico;
- o hash histórico é igual ao hash corrente em todos os casos;
- `captured_at = first_seen_at` em todos os casos;
- `first_seen_at = last_seen_at` em todos os casos.
Assim, não há série histórica real ainda. Não é possível medir mudança de preço, republicação, duração, saída ou retorno do estoque.
### Execução do scraper
`crawl_runs` registra horários, estado e contadores JSON, mas não existe `run_id` em `listings`, `listing_history` ou `listing_searches`. A execução geradora só pode ser inferida por horário. Isso limita auditoria, cobertura por execução e diagnóstico de ausências. Cobertura técnica: 13–21/07/2026. Há observações de anúncios em quatro datas, janela insuficiente para sazonalidade ou liquidez.
### Imóvel físico | Região | Acumulados | Ativos | Inativos |
|---|---:|---:|---:|
| Jardins Mangueiral | 94 | 78 | 16 |
| Cruzeiro Novo | 67 | 63 | 4 |
Não existe tabela ou chave para imóvel físico, unidade, endereço normalizado ou relação de duplicidade. Anúncios de corretores diferentes permanecem independentes. A base também não contém hashes perceptuais; possui apenas SHA-256 exato das imagens. A execução 20, em 21/07, processou oito páginas, encontrou 141 URLs, concluiu 141 detalhes sem falha, baixou 572 fotos novas, reutilizou 4.404 e marcou 20 anúncios como inativos. Ausência da busca não comprova venda.
Logo, as unidades válidas hoje são: ## 4. O que os dados representam
- anúncio único: suportado por `listing_id`; - `listings`: identidade de anúncio e estado corrente;
- estado alterado do anúncio: suportado por `listing_history`; - `listing_history`: estados distintos por `content_hash`, não cada observação;
- imóvel físico provável/confirmado: **não suportado sem derivação e revisão**. - `listing_searches`: vínculo entre anúncio e busca;
- `photos`: mídia associada ao anúncio, sem vínculo a um snapshot específico;
- `crawl_runs`: execução técnica sem chave direta para os anúncios observados;
- imóvel físico provável: não existe como entidade persistida e depende de deduplicação derivada.
## 5. Qualidade dos dados Há 21 anúncios com um snapshot, 6 com dois, 7 com três e 127 com quatro. Essa distribuição não deve ser interpretada como número de mudanças reais.
### 5.1 Integridade estrutural ## 5. Auditoria das mudanças históricas
Pontos positivos: Foram comparadas as transições consecutivas de `data_json`:
- arquivo íntegro segundo `quick_check`; | Campo alterado | Transições |
- nenhuma FK órfã em histórico, buscas ou fotos; |---|---:|
- todos os JSONs são sintaticamente válidos; | `content_hash` | 401 |
- todos os IDs de anúncio têm 6–7 caracteres numéricos; | `creci` | 398 |
- todas as URLs pertencem ao domínio e padrão esperados; | `advertiser_code` | 66 |
- todos os 156 HTMLs brutos referenciados existem; | `neighborhood` | 25 |
- todos os hashes de conteúdo têm 64 caracteres; | `description` | 11 |
- não há URL, hash corrente ou caminho de HTML repetido. | `price_brl` | 3 |
| `address` | 3 |
Limitações estruturais:
- nenhuma relação entre execução e item;
- fonte implícita, sem `source_id`;
- área armazenada em um único conceito;
- mídia ligada ao anúncio, não ao snapshot;
- ausência de entidade de imóvel físico e de localização normalizada;
- apenas autoíndices; não há índices por datas ou atributos analíticos.
### 5.2 Completude dos principais campos
| Campo | Ausentes | Percentual |
|---|---:|---:|
| preço | 0 | 0,0% |
| área | 0 | 0,0% |
| quartos | 0 | 0,0% |
| condomínio | 59 | 37,8% |
| IPTU | 144 | 92,3% |
| suítes | 41 | 26,3% |
| vagas | 57 | 36,5% |
| cidade | 156 | 100,0% |
| UF | 156 | 100,0% |
| nome do anunciante | 156 | 100,0% |
| latitude/longitude | 156 / 156 | 100,0% / 100,0% |
| data de publicação | 156 | 100,0% |
| data de inativação | 156 | 100,0% |
Esses percentuais medem apenas nulos. Para campos textuais, a taxa de preenchimento é enganosa porque o parser capturou conteúdo da página em vez do valor pretendido.
### 5.3 Contaminação de texto
Os problemas mais graves são:
- 146 endereços (93,6%) têm mais de 300 caracteres;
- 154 bairros (98,7%) têm mais de 100 caracteres;
- os dois valores curtos de bairro são `.` e um trecho de descrição, também inválidos;
- 98 códigos de anunciante (62,8%) têm mais de 100 caracteres;
- 153 valores de CRECI (98,1%) têm mais de 100 caracteres.
Os textos longos incluem navegação, formulários, política de privacidade e outros fragmentos da página. Eles podem conter dados de contato exibidos pelo portal; por isso, não foram reproduzidos neste relatório. Esses campos devem ser considerados **indisponíveis**, não apenas ruidosos.
Os títulos têm somente quatro valores distintos e funcionam como títulos genéricos por tipologia. As 156 descrições são distintas, mas têm apenas 156–161 caracteres, compatíveis com resumos/metadados truncados, não com a descrição completa. `extra_json` contém um array `json_ld` vazio nos 156 registros e não resolve a reextração.
### 5.4 Valores estruturados e outliers
| Tipo anunciado | N | Preço mínimo | Preço máximo | Área mínima | Área máxima | Quartos |
|---|---:|---:|---:|---:|---:|---:|
| Apartamento | 65 | R$ 480.000 | R$ 2.030.598 | 61 m² | 113 m² | 3 |
| Casa | 91 | R$ 450.000 | R$ 570.000.000 | 0,11 m² | 131,46 m² | 3–4 |
Não há valores nulos ou não positivos em preço e área, mas isso não significa validade:
- anúncio `1351663`: R$ 570.000.000, forte indício de erro de escala ou parsing; Em 305 transições, somente `creci` e o hash mudaram. Isso indica instabilidade do parser ou conteúdo variável da página. Não há base para tratar 401 transições como mudanças de mercado.
- anúncio `1370918`: 0,11 m², área fisicamente implausível.
Não foi aplicado corte nem correção silenciosa. Também não se calculou preço por m², pois `area_m2` não distingue área útil, privativa, construída, total ou terreno. Mudanças reais de preço identificadas:
### 5.5 Fotos e arquivos brutos - `1173275`: R$ 550.000 para R$ 549.000 em 21/07;
- `1348535`: R$ 950.000 para R$ 880.000 em 20/07, mantido em 21/07;
- `1372539`: R$ 572.500 para R$ 569.000 em 17/07, mantido depois.
Das 5.386 linhas em `photos`: Esses valores são preços anunciados, não negociados.
- 5.383 têm arquivo local, e o tamanho em disco coincide com `bytes`; ## 6. Qualidade dos campos
- 3 não têm hash, MIME, tamanho ou caminho, correspondendo às três falhas registradas pelo coletor;
- há 4.514 WebP, 558 JPEG, 311 PNG e 3 sem MIME;
- os anúncios possuem de 5 a 61 fotos;
- não há caminho local nem ordinal duplicado dentro de anúncio.
Há 3.307 hashes distintos. Cento e quatorze hashes aparecem em mais de um anúncio, e um deles aparece nos 156 anúncios. Isso revela logos, selos, placeholders ou outros ativos comuns. Hash idêntico pode ser sinal de anúncio duplicado somente depois de excluir ativos genéricos e combinar outros sinais. Nos 161 anúncios:
## 6. Duplicidades e imóveis físicos prováveis - preço, área e quartos estão preenchidos em todos;
- cidade, UF, latitude e longitude estão ausentes em todos;
- 150 endereços têm mais de 300 caracteres;
- 159 bairros têm mais de 100 caracteres;
- permanece um preço de R$ 570 milhões (`1351663`);
- permanece uma área de 0,11 m² (`1370918`);
- nenhum registro de foto está sem hash ou caminho local no snapshot atual.
- a triagem perceptual com Pillow 12.3.0 foi reexecutada nos dois recortes; recuperou os grupos já documentados e não identificou nova duplicação suficientemente sustentada entre os 30 finalistas.
Não há duplicidade técnica imediata por `listing_id`, URL ou `content_hash`. Uma assinatura exploratória composta por bairro, tipo, preço, área, quartos e vagas encontrou 13 grupos, equivalentes a 18 linhas excedentes. Esse resultado **não é evidência de 18 imóveis duplicados**, porque: O preenchimento não garante validade. Localização e anunciante continuam contaminados. Área não distingue privativa, útil, construída, total, terreno ou ampliação.
- o bairro está contaminado; ## 7. Disponibilidade e quarentena visual
- preço, área e quartos podem coincidir entre unidades distintas;
- fotos compartilhadas incluem ativos genéricos;
- não há endereço/unidade confiável nem telefone normalizado;
- SHA-256 só detecta arquivos idênticos, não versões redimensionadas da mesma foto.
Consequentemente, o método de deduplicação desta avaliação foi apenas técnico: unicidade de anúncio por `listing_id`. Nenhuma fusão de anúncios nem contagem de imóveis físicos foi produzida. Vinte anúncios estão inativos no banco, mas retirada não prova venda. Além disso, dez anúncios ainda ativos exibem indicação explícita na primeira foto:
Uma futura camada derivada deve combinar, com confiança e revisão manual: - Cruzeiro: `1263868`;
- Mangueiral: `1301400`, `1346017`, `1346231`, `1349519`, `1351729`, `1352943`, `1356197`, `1362968` e `1366801`.
- endereço/QC/quadra/bloco/unidade reextraídos; Esses registros ficam em quarentena histórica como “vendido visualmente indicado”. O selo é evidência operacional, não prova registral.
- imagens após remoção de ativos comuns, idealmente com hash perceptual;
- preço, área, planta, quartos e vagas validados;
- anunciante e descrição normalizados;
- estados `unreviewed`, `probable`, `confirmed`, `rejected` e `ambiguous`.
## 7. Possíveis quebras de coleta ## 8. Atualização dos rankings
1. **Localização:** `address` e `neighborhood` capturaram grandes trechos da página; cidade, UF e coordenadas ficaram vazias. O Mangueiral não recebeu anúncio novo em 21/07. `1356197`, antes em 9º, passou a exibir **VENDIDO** na primeira foto. Depois do feedback pessoal, o funil foi refeito sobre os 78 ativos; `1377683`, `1371718` e `1140317` entraram no Top 5 por aderência inferida. A auditoria integral também identificou `1301400` com selo de vendido na primeira foto e `1369473` como solicitação de compra, não oferta.
2. **Anunciante:** `advertiser_code` e `creci` frequentemente capturaram texto extenso; `advertiser_name` ficou vazio.
3. **Dados estruturados:** o `json_ld` foi preservado como array vazio em todos os anúncios.
4. **Escala numérica:** pelo menos um preço e uma área são claramente suspeitos.
5. **Fotos:** três falhas foram registradas de modo consistente, sem arquivo local.
6. **Execuções vazias:** duas execuções `ok` não coletaram páginas. Sem motivo/configuração persistidos, não é possível diferenciar teste, filtro vazio ou falha silenciosa.
7. **Filtro do Mangueiral:** a URL coletada inclui casas de quatro quartos e não identifica planta original.
## 8. Limitações para qualquer conclusão imobiliária O Cruzeiro recebeu:
- Um único dia não permite análise histórica ou tendência. - `1381514`: SHCES 301, R$ 690 mil, 4º andar, reforma aparente forte, sem confirmação de elevador ou vaga e com divergência entre 63 e 93 m²;
- A amostra cobre apenas DF Imóveis e duas buscas específicas. - `1400223`: SHCES 609 D, R$ 495 mil, 2º andar, boa luz e necessidade de reforma ampla.
- Preço anunciado não é preço negociado.
- Não há transações, status comprovado de venda ou evidência de fechamento.
- Não há imóveis físicos deduplicados.
- Não há localização analítica confiável por QC, quadra ou bloco.
- Não há conceito comparável de área.
- O estado de reforma, planta original, elevador, garagem, andar e qualidade funcional não está modelado de forma suficiente.
- Para Jardins Mangueiral, `bedrooms = 3` não valida planta original de três quartos.
Portanto, ainda não é apropriado produzir medianas regionais, comparáveis, preço por m², tempo de anúncio, liquidez ou recomendações de visita com base apenas neste banco. Os dois entraram no Top 15 condicionado. `1375199` saiu por inatividade; `1370891` passou para “quase entrou” pela nova evidência, não por piora comprovada.
## 9. Próximas ações recomendadas ## 9. Limitações
Antes da análise dos imóveis: - um único portal e duas buscas;
- janela histórica curta;
- 20 inativos sem causa confirmada;
- snapshots inflados por campos textuais instáveis;
- ausência de entidade de imóvel físico;
- localização estruturada não confiável;
- área semanticamente inconsistente;
- fotos podem conter material genérico e republicações;
- planta original, sistemas, ruído e documentação exigem validação externa.
1. corrigir e testar a extração de endereço, bairro, anunciante e CRECI usando os HTMLs já salvos, sem recrawling; Não calcular preço por m² amplo, liquidez ou tendência com este snapshot.
2. criar uma derivação reproduzível fora do banco original e preservar os valores brutos;
3. separar áreas privativa, útil, construída, total e de terreno, deixando nulo o conceito não comprovado;
4. criar regras explícitas de validação para preço e área, mantendo os outliers identificados em quarentena;
5. filtrar o Mangueiral por planta original de três quartos, não apenas pelo número anunciado;
6. coletar múltiplas datas e auditar a cobertura por busca antes de estimar permanência, entradas ou saídas;
7. numa evolução aprovada do coletor, adicionar `source_id`, `run_id` nos snapshots, referência ao HTML bruto histórico e versão/configuração da coleta;
8. construir `property_candidates` apenas em uma camada derivada, com confiança e revisão, sem fundir registros no banco original.
## 10. Consultas de reprodução ## 10. Reprodução
Todas devem ser executadas com `sqlite3 -readonly dfimoveis_data/dfimoveis.sqlite3` ou conexão Python URI em `mode=ro` e `PRAGMA query_only = ON`. Consultas essenciais, sempre em leitura:
```sql ```sql
-- Objetos do esquema PRAGMA query_only = ON;
PRAGMA quick_check;
PRAGMA foreign_key_check;
SELECT type, name, tbl_name, sql SELECT type, name, tbl_name, sql
FROM sqlite_master FROM sqlite_master
ORDER BY type, name; ORDER BY type, name
LIMIT 50;
-- Contagens (executar uma tabela por vez após validar os nomes)
SELECT COUNT(*) FROM crawl_runs;
SELECT COUNT(*) FROM listings; SELECT COUNT(*) FROM listings;
SELECT COUNT(*) FROM listing_history; SELECT COUNT(*) FROM listing_history;
SELECT COUNT(*) FROM listing_searches; SELECT COUNT(*) FROM listing_searches;
SELECT COUNT(*) FROM photos; SELECT COUNT(*) FROM photos;
SELECT COUNT(*) FROM crawl_runs;
-- Cobertura SELECT s.search_url,
SELECT MIN(started_at), MAX(finished_at), COUNT(*) COUNT(*) AS accumulated,
FROM crawl_runs; SUM(l.inactive_at IS NULL) AS active,
SUM(l.inactive_at IS NOT NULL) AS inactive
SELECT MIN(first_seen_at), MAX(last_seen_at), FROM listing_searches AS s
COUNT(DISTINCT date(first_seen_at)) JOIN listings AS l USING (listing_id)
FROM listings; GROUP BY s.search_url
LIMIT 10;
-- Número de snapshots por anúncio
SELECT snapshots, COUNT(*) AS listings
FROM (
SELECT listing_id, COUNT(*) AS snapshots
FROM listing_history
GROUP BY listing_id
)
GROUP BY snapshots;
-- Completude de campos críticos
SELECT
COUNT(*) AS total,
SUM(price_brl IS NULL) AS missing_price,
SUM(area_m2 IS NULL) AS missing_area,
SUM(condominium_brl IS NULL) AS missing_condominium,
SUM(iptu_brl IS NULL) AS missing_iptu,
SUM(latitude IS NULL OR longitude IS NULL) AS missing_coordinates
FROM listings;
-- Contaminação textual sem imprimir os textos
SELECT
SUM(length(address) > 300) AS long_address,
SUM(length(neighborhood) > 100) AS long_neighborhood,
SUM(length(advertiser_code) > 100) AS long_advertiser_code,
SUM(length(creci) > 100) AS long_creci
FROM listings;
-- Integridade referencial e física
PRAGMA quick_check;
PRAGMA foreign_key_check;
``` ```
## 11. Implicação para a decisão de compra ## 11. Implicação prática
Esta avaliação reduz o risco de transformar uma coleta tecnicamente bem-sucedida em falsa precisão de mercado. O próximo ganho real não virá de comparar preços imediatamente, mas de corrigir localização e semântica de área, acumular histórico e construir uma deduplicação reversível. Até lá, os registros são úteis como inventário bruto de anúncios e arquivos, não como avaliação consolidada de imóveis físicos. O snapshot melhora a triagem operacional, sobretudo ao revelar inativos e dois apartamentos novos. Ele ainda não justifica pressa nem conclusões de mercado. O ganho imediato vem de manter quarentenas visuais, corrigir a geração excessiva de snapshots e validar documentalmente os candidatos antes de visita ou negociação.

184
reports/mangueiral_top10/README.md

@ -1,184 +0,0 @@
# Ranking preliminar — casas no Jardins Mangueiral
**Data de referência do snapshot:** 17/07/2026
**Relatório revisado:** 17/07/2026
**Unidade principal:** imóvel físico provável, não snapshot nem anúncio isolado
**Finalidade:** priorizar visitas; não estimar preço negociado nem substituir vistoria ou validação documental
## Pergunta
Quais são as dez casas do Jardins Mangueiral que mais merecem atenção segundo as preferências e regras do projeto, dando às fotografias importância comparável à dos dados estruturados?
## Universo e método
Foram avaliados os 94 anúncios acumulados encontrados pela busca de casas no Jardins Mangueiral: 84 estavam ativos e 10 inativos no snapshot consistente de 17/07/2026. A base cobre um único portal. Há 159 anúncios nas duas regiões, 295 snapshots de conteúdo e 6.272 mídias; 133 anúncios têm dois snapshots, mas quatro mudanças foram materialmente relevantes nos campos imobiliários e apenas uma delas foi preço.
Como o banco estava em WAL, ele foi aberto com `mode=ro`, sem `immutable=1`, dentro de uma transação de leitura consistente e com `PRAGMA query_only = ON`. Entre os anúncios ativos, 80 informam três quartos e preço plausível entre R$ 450 mil e R$ 1,15 milhão. O anúncio de R$ 1,15 milhão descreve outra implantação, no condomínio American Garden, e não a casa-padrão de 68 m² das QCs; não é comparável ao universo prioritário. Esses números descrevem anúncios, não imóveis únicos nem preços negociados.
Não foi calculado preço por metro quadrado: os campos e descrições misturam área privativa, construída, total e de terreno. Tampouco foi criada pontuação numérica, porque os pesos não foram aprovados. A ordem é uma **prioridade de visita ajustada por risco**, considerando em conjunto:
1. compatibilidade provável com a planta original de três quartos;
2. funcionalidade, circulação, armazenamento e flexibilidade de longo prazo;
3. qualidade real da conversão do quintal, sem penalizar fechamento automaticamente;
4. luz e ventilação aparentes, conservação e coerência dos acabamentos;
5. risco e transtorno de adequação;
6. preço, condomínio, IPTU, vagas e área anunciados;
7. QCs citadas nos relatos de moradores, apenas como evidência anedótica;
8. completude, coerência e confiabilidade das fotos.
### Filtro da planta original
Os dez imóveis abaixo têm fachada, posição de escada, distribuição e dormitórios fotografados compatíveis com a tipologia padrão de três quartos, e nenhum deles é descrito como conversão de dois para três quartos. Isso sustenta apenas a classificação **planta original provável**. A confirmação exige planta, matrícula ou documentação da unidade. O anúncio 1374012, por exemplo, foi retirado da disputa apesar do preço baixo porque a própria descrição informa “3 quartos sendo 1 adaptado”.
### Revisão fotográfica
Em 17/07/2026, a primeira foto de todos os escolhidos foi auditada especificamente para marcas de “vendido” ou “negócio fechado”. Quatro anúncios que chegaram a integrar versões do ranking foram removidos por esse critério: `1346231`, `1349519`, `1351729` e `1366801`.
A revisão anterior havia examinado 1.892 arquivos distintos. Nesta atualização foram revisadas as imagens iniciais dos novos anúncios elegíveis e, em detalhe, 15 imagens coerentes do novo finalista `1397378`; imagens posteriores da galeria que pertencem visualmente a outros imóveis foram descartadas como contaminação. As plantas oficiais de duas e três unidades de três quartos e os mapas locais em `resources/` também foram confrontados com a galeria e a QC.
As fotos foram usadas para avaliar coerência da planta, presença dos três dormitórios, circulação, luz, ventilação aparente, conservação, cozinha, banheiros, lavanderia, quintal, integração social e qualidade das ampliações. Elas não validam elétrica, hidráulica, impermeabilização, estrutura, calor, ruído ou regularização.
As galerias baixadas frequentemente continuam, depois das imagens do imóvel, com recomendações do portal, fotos de outras casas, selos de “vendido”, publicidade e logos. Esses arquivos foram vistos, identificados pela quebra de coerência visual e desconsiderados. Por isso, quantidade bruta de fotos não foi tratada como qualidade de evidência, e hashes compartilhados foram usados apenas para triagem manual de duplicidade.
## Ranking
### 1. Anúncio 1279892 — QC 10
- **Anunciado:** R$ 595.000; 68 m² no campo estruturado e 107 m² na descrição; condomínio R$ 383; IPTU R$ 1.050; 3 quartos, 1 suíte; 1 vaga informada no banco.
- **Por que lidera:** oferece o melhor equilíbrio entre preço, localização considerada boa nos relatos, reforma pronta e planta visualmente coerente. A diferença para casas mais caras parece estar mais na ausência de mobiliário do que em obra obrigatória.
- **Fotos:** ambientes claros, sala ampla, cozinha planejada bem executada, banheiros atualizados e três dormitórios reconhecíveis. O fundo combina área coberta e trechos abertos, preservando luz e ventilação; não aparece uma segunda cozinha completa redundante.
- **Pendências:** esclarecer os conceitos de 68 e 107 m², a DCE citada, regularização das ampliações, número jurídico de vagas e impermeabilização da cobertura translúcida.
- **Ação:** candidato forte; visitar primeiro.
- **URL:** https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-rua-f-1279892
### 2. Anúncio 1350367 — QC 6
- **Anunciado:** R$ 569.000; 68 m² no banco, 107 m² totais e terreno de 131 m² na descrição; condomínio R$ 320; 3 quartos, 1 suíte; 2 vagas.
- **Por que está no topo:** preço baixo para o grupo final, QC considerada alternativa prática e ampliação social que parece efetivamente utilizável.
- **Fotos:** casa vazia, clara e aparentemente reformada; banheiros e pisos coerentes; três dormitórios aparentes. O quintal foi convertido em uma varanda ampla com churrasqueira e bancada, mas mantém abertura lateral e circulação, em vez de virar cômodo escuro ou segunda cozinha fechada.
- **Pendências:** confirmar área construída, licenças, ventilação e calor sob a cobertura, drenagem, elétrica da ampliação e o que falta de marcenaria/mobiliário.
- **Ação:** candidato forte; visitar.
- **URL:** https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-6-rua-c-1350367
### 3. Anúncio 1346017 — QC 10
- **Anunciado:** R$ 577.000; 68 m²; condomínio e IPTU ausentes; 3 quartos, 1 suíte; 2 vagas.
- **Por que se destaca:** casa pronta, luminosa e pouco personalizada por preço competitivo, em QC considerada boa nos relatos.
- **Fotos:** sala e jantar funcionais, cozinha planejada, três quartos claros e banheiros conservados. O quintal permanece majoritariamente aberto, com cobertura leve e uso social simples; é uma das soluções menos arriscadas para ventilação e manutenção.
- **Pendências:** confirmar proteção contra chuva, privacidade, área de serviço, taxas, orientação solar e regularização de qualquer cobertura.
- **Ação:** candidato forte; visitar.
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1346017
### 4. Anúncio 1397378 — QC 8
- **Anunciado:** R$ 600.000; 68 m²; condomínio R$ 316,14; IPTU R$ 1.119,67; 3 quartos, 1 suíte; 2 vagas.
- **Por que se destaca:** QC 8 tem acesso prático a comércio e serviços nos mapas fornecidos, e a casa combina preço intermediário com implantação próxima da planta original de três quartos.
- **Fotos:** sala e cozinha claras, marcenaria utilizável, três dormitórios aparentes e escada/andar superior compatíveis com a planta de 68 m². O fundo mantém trecho aberto, corredor lateral e cobertura parcial, sem transformar todo o quintal em segunda cozinha. Há sinais de acabamento simples e manutenção pontual, mas não de reforma integral imediata.
- **Pendências:** confirmar planta e documentos, origem das marcas/acabamentos, drenagem linear, impermeabilização da cobertura, orientação solar e itens de marcenaria que permanecem. Parte final da galeria está contaminada por fotos de outros imóveis e foi desconsiderada.
- **Ação:** candidato forte; visitar depois dos três primeiros.
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-8-rua-b-1397378
### 5. Anúncio 1359960 — QC 7
- **Anunciado:** R$ 610.000; 68 m²; condomínio R$ 339; IPTU R$ 1.128,19; 3 quartos, 1 suíte; 2 vagas.
- **Por que entra:** QC considerada alternativa prática e conversão do quintal que cria uma sala social ampla, em vez de apenas instalar equipamentos com rótulo “gourmet”.
- **Fotos:** boa sala frontal, cozinha planejada, três quartos aparentes e grande espaço coberto nos fundos, hoje usado para descanso e convivência. Não foi identificada segunda cozinha completa; a ampliação parece flexível e mobiliável.
- **Pendências:** o fechamento é extenso; testar calor, ventilação cruzada, iluminação noturna, exaustão, acústica e impermeabilização antes de valorizá-lo.
- **Ação:** visitar com foco no conforto ambiental da ampliação.
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-7-1359960
### 6. Anúncio 1319996 — QC 14
- **Anunciado:** R$ 649.000; 68 m²; condomínio e IPTU ausentes; 3 quartos, 1 suíte; 2 vagas.
- **Por que se destaca:** uma das reformas mais completas e coerentes, com placas solares e boa separação entre convivência e lavanderia.
- **Fotos:** acabamento uniforme, marcenaria abundante, três dormitórios aparentes, banheiros atualizados e área externa coberta com bancada, churrasqueira, abertura lateral e circulação suficiente para receber pessoas.
- **Pendências:** QC 14 não está entre as QCs priorizadas pelos relatos; confirmar taxas, homologação das placas, ventilação/exaustão, manutenção da cobertura e escopo técnico da reforma.
- **Ação:** visitar como referência de imóvel pronto.
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1319996
### 7. Anúncio 1324014 — QC 12
- **Anunciado:** R$ 649.000; 68 m²; condomínio e IPTU ausentes; 3 quartos, 1 suíte; vaga não extraída.
- **Por que entra:** acabamento interno e marcenaria fortes, com área social coberta bem executada e condição aparente de ocupação sem reforma relevante.
- **Fotos:** cozinha, sala e banheiros atualizados; a área gourmet é ampla e iluminada, mas concentra churrasqueira, bancada e apoio muito próximos da cozinha principal.
- **Pendências:** confirmar vaga, planta original, ventilação, exaustão, conforto térmico, drenagem, regularização da cobertura e se a duplicação de equipamentos melhora de fato a rotina.
- **Ação:** visitar depois dos candidatos mais baratos e das QCs prioritárias.
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-12-rua-k-1324014
### 8. Anúncio 1333569 — QC 6
- **Anunciado:** R$ 650.000; 90 m²; condomínio e IPTU ausentes; 3 quartos, 1 suíte; 2 vagas.
- **Por que entra:** QC considerada alternativa prática e ampliação social já em uso, com abertura superior e lateral que pode mitigar o fechamento do quintal.
- **Fotos:** casa ocupada, bem conservada e funcional; a área externa reúne jantar, estar e churrasqueira, com cobertura aparentemente translúcida/retrátil. Os cômodos íntimos parecem utilizáveis, embora menos bem documentados que nos primeiros colocados.
- **Pendências:** confirmar se os 90 m² são comparáveis, funcionamento e manutenção da cobertura, exaustão, calor, iluminação, taxas e regularização.
- **Ação:** visitar depois dos oito primeiros.
- **URL:** https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-6-1333569
### 9. Anúncio 1359480 — QC 4
- **Anunciado:** R$ 535.000; 70 m²; condomínio R$ 330; IPTU ausente; 3 quartos, 1 suíte; 2 vagas.
- **Por que entra:** é a alternativa de menor preço entre os finalistas habitáveis e preserva margem financeira para custos de aquisição e adequações.
- **Fotos:** sala integrada utilizável, cozinha e banheiros funcionais e três dormitórios aparentes. O quintal/lavanderia têm acabamento mais simples que os líderes, mas não sugerem obra estrutural imediata nas imagens.
- **Pendências:** QC 4 precisa provar conveniência logística; confirmar planta original, área de 70 m², documentação, drenagem, impermeabilização e orçamento de acabamento externo.
- **Ação:** visitar como referência de valor e limite inferior de preço.
- **URL:** https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-4-1359480
### 10. Anúncio 1356197 — QC 4
- **Anunciado:** R$ 649.000; 68 m²; condomínio R$ 316; IPTU ausente; 3 quartos, 1 suíte; 2 vagas.
- **Por que entra:** casa vazia com acabamento interno forte e condição aparente de ocupação sem reforma relevante.
- **Fotos:** sala, cozinha, banheiros e marcenaria bem conservados; boa parte do espaço coberto parece funcionar como garagem, com menor evidência de quintal social útil.
- **Pendências:** confirmar planta original, uso e ventilação do espaço coberto, drenagem, impermeabilização, regularização e conveniência da QC 4.
- **Ação:** visitar depois dos nove primeiros.
- **URL:** https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-4-1356197
## Quase entraram
- **[1368562](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-5-1368562), QC 5, R$ 720.000:** ampliação de aproximadamente 110 m² bem executada e ventilada por cobertura translúcida, mas o prêmio de preço e a QC menos prioritária reduzem a urgência da visita.
## Quarentenas e exclusões relevantes
- **1351729:** a primeira foto declara “VENDIDO” e “negócio fechado”. Isso não comprova registro ou preço da transação, mas é evidência suficiente de indisponibilidade operacional; removido do ranking de visitas por correção do usuário em 17/07/2026.
- **1349519:** a primeira foto também declara “VENDIDO” e “negócio fechado”, com o código correspondente ao anúncio. Removido do ranking de visitas por correção do usuário em 17/07/2026.
- **1366801:** a primeira foto declara “VENDIDO” e “negócio fechado”, com o código 1468 correspondente ao anúncio. Removido do ranking de visitas por correção do usuário em 17/07/2026.
- **1346231:** a primeira foto declara “VENDIDO” e “negócio fechado”, com o código 1428 correspondente ao anúncio. Removido após auditoria sistemática das primeiras fotos em 17/07/2026.
- **1350829 e 1358463:** anunciados com quatro quartos; fora do filtro.
- **1374012:** a descrição declara que um dos três quartos é adaptado; não há prova suficiente de planta original de três quartos.
- **1351663:** preço estruturado de R$ 570 milhões, erro evidente de escala; excluído até correção.
- **1369473 e 1336887:** galerias dominadas por material genérico ou de outros anúncios, sem evidência suficiente do imóvel.
- **1301400 e 1201070:** fotos com marcação de vendido/permuta; tratadas como inconsistência, não prova de venda.
- **1041635 e 1383513:** mesmo imóvel provável pelas fotos; a repetição não foi contada como duas opções.
- **1239730, 1242337, 1294027, 1359192, 1359662 e 1367341:** grupo de republicações prováveis da mesma unidade; a oferta não foi inflada.
- **1354016 e 1377683:** duplicata provável após revisão visual; seria uma única opção, não duas.
- **1356153, 1358854 e 1358920:** grupo provável de uma mesma casa vazia.
- **1348449 e 1351461:** descrições e composição praticamente idênticas na QC 13; tratar como mesmo imóvel até confirmação.
## Qualidade e limitações
- O banco contém anúncios e snapshots de conteúdo, não imóveis físicos confirmados. Os dois pontos observados ainda são insuficientes para tendência ou tempo real de mercado.
- Entre os 84 anúncios ativos do Mangueiral, condomínio falta em 40 e IPTU em 74; não foi possível comparar custo mensal com consistência.
- Área existe em todos os registros, mas não é semanticamente comparável. Os próprios finalistas exibem divergências entre banco e descrição.
- A busca e as galerias contêm material contaminante do portal. Duplicidades foram classificadas manualmente e permanecem probabilísticas.
- Anúncio, foto e descrição não confirmam planta original, regularização da ampliação, documentação, estrutura ou sistemas.
- As preferências por QC vêm de relatos anedóticos e precisam ser testadas em deslocamentos e visitas reais.
- A referência financeira de R$ 650 mil a R$ 750 mil não foi assumida como atual. O ranking prioriza visitas, não define capacidade de compra ou preço máximo.
## Verificações obrigatórias nas visitas
1. solicitar planta original, matrícula, habite-se e documentos das ampliações;
2. medir os três quartos e conferir posição da escada, fachada e implantação;
3. testar calor, ventilação, luz e ruído com portas e janelas fechadas e abertas;
4. inspecionar telhado, rufos, calhas, impermeabilização, drenagem e sinais de infiltração;
5. testar quadro elétrico, aterramento, tomadas, hidráulica, pressão e escoamento;
6. verificar se a área gourmet duplica a cozinha ou realmente melhora a rotina;
7. confirmar condomínio, IPTU, vagas, taxas extras, regras de obra e atas;
8. percorrer a QC em horários relevantes para comércio, trânsito, segurança, ruído e estacionamento;
9. só depois estimar adequação e definir preço máximo de negociação.
## Reprodutibilidade
- `analysis.py`: extrai banco e HTMLs em leitura, produzindo TSV.
- `photo_similarity.py`: sinaliza pares por hash visual simples; exige revisão humana e sofre com imagens genéricas do portal.
- `query.sql`: consultas principais de universo e qualidade.
- `parameters.json`: parâmetros e fingerprint do snapshot.
- `results.csv`: ranking em formato tabular.

BIN
reports/mangueiral_top10/__pycache__/analysis.cpython-312.pyc

Binary file not shown.

BIN
reports/mangueiral_top10/__pycache__/analysis.cpython-314.pyc

Binary file not shown.

BIN
reports/mangueiral_top10/__pycache__/photo_similarity.cpython-312.pyc

Binary file not shown.

BIN
reports/mangueiral_top10/__pycache__/photo_similarity.cpython-314.pyc

Binary file not shown.

29
reports/mangueiral_top10/parameters.json

@ -1,29 +0,0 @@
{
"analysis_date": "2026-07-17",
"listing_data_reference_date": "2026-07-17",
"database_path": "dfimoveis_data/dfimoveis.sqlite3",
"database_main_file_sha256": "7a5102a62bc66a344bd9440fa36301d10a57711bcdd263b767b65e88f70957f4",
"logical_snapshot_sha256": "fe604aab8af7f67c833e6de2a0fc81c7dc2950ca440ad7b23ebd03d0c2546188",
"database_mode": "read_only_transaction_with_wal",
"search_url_fragment": "/jardins-mangueiral/",
"listing_limit": 150,
"observed_listing_count": 94,
"active_listing_count": 84,
"inactive_listing_count": 10,
"database_crawl_run_count": 16,
"new_listing_count_since_previous_report": 3,
"active_plausible_price_three_bedroom_count": 80,
"ranking_unit": "probable_physical_property",
"numeric_score_used": false,
"original_three_bedroom_requires_document_confirmation": true,
"photo_first_pass_limit_per_listing": 16,
"photo_first_pass_files": 1449,
"photo_finalist_count": 19,
"photo_finalist_files": 747,
"photo_files_reviewed_distinct_cumulative": 1907,
"new_finalist_photos_reviewed": 15,
"plans_reviewed": 2,
"map_captures_reviewed": 6,
"first_photo_sale_marker_audit_count": 10,
"visually_unavailable_listing_ids": ["1346231", "1349519", "1351729", "1366801"]
}

11
reports/mangueiral_top10/results.csv

@ -1,11 +0,0 @@
rank,representative_listing_id,probable_duplicate_ids,qc,price_brl,area_m2,bedrooms,suites,parking_spaces,original_three_bedroom_status,decision
1,1279892,,10,595000,68,3,1,1,probable_pending_documents,strong_candidate
2,1350367,,6,569000,68,3,1,2,probable_pending_documents,strong_candidate
3,1346017,,10,577000,68,3,1,2,probable_pending_documents,strong_candidate
4,1397378,,8,600000,68,3,1,2,probable_pending_documents,strong_candidate
5,1359960,,7,610000,68,3,1,2,probable_pending_documents,visit_and_test_closed_yard
6,1319996,,14,649000,68,3,1,2,probable_pending_documents,visit
7,1324014,,12,649000,68,3,1,,probable_pending_documents,visit
8,1333569,,6,650000,90,3,1,2,probable_pending_documents,visit_after_top_eight
9,1359480,,4,535000,70,3,1,2,probable_pending_documents,visit_as_value_reference
10,1356197,,4,649000,68,3,1,2,probable_pending_documents,visit_after_top_nine
1 rank representative_listing_id probable_duplicate_ids qc price_brl area_m2 bedrooms suites parking_spaces original_three_bedroom_status decision
2 1 1279892 10 595000 68 3 1 1 probable_pending_documents strong_candidate
3 2 1350367 6 569000 68 3 1 2 probable_pending_documents strong_candidate
4 3 1346017 10 577000 68 3 1 2 probable_pending_documents strong_candidate
5 4 1397378 8 600000 68 3 1 2 probable_pending_documents strong_candidate
6 5 1359960 7 610000 68 3 1 2 probable_pending_documents visit_and_test_closed_yard
7 6 1319996 14 649000 68 3 1 2 probable_pending_documents visit
8 7 1324014 12 649000 68 3 1 probable_pending_documents visit
9 8 1333569 6 650000 90 3 1 2 probable_pending_documents visit_after_top_eight
10 9 1359480 4 535000 70 3 1 2 probable_pending_documents visit_as_value_reference
11 10 1356197 4 649000 68 3 1 2 probable_pending_documents visit_after_top_nine

70
reports/mangueiral_top15/README.md

@ -0,0 +1,70 @@
# Top 15 — Jardins Mangueiral
**Snapshot:** 22/07/2026 (execução 22, concluída às 21h34 no horário de Brasília)
**Objetivo:** priorizar investigação na região principal do projeto, somente entre casas com planta original provável de três quartos e horizonte de 10–20 anos.
## Universo e método
O banco foi aberto somente para leitura. Há 162 anúncios, 638 observações históricas e 7.457 registros históricos de foto; apenas os 3.521 registros marcados como galeria primária foram admitidos como evidência fotográfica. No Mangueiral são 95 anúncios acumulados, 78 ativos e 17 inativos. A coleta cobre 13–22/07/2026. Foram separados anúncio, imóvel provável e observação histórica, deduplicando republicações por fotos, descrição e implantação.
Desde o relatório anterior, `1401074` entrou no universo e `1290512` ficou inativo; a ausência não prova venda. A execução 22 examinou 78 anúncios sem falhas de detalhe e registrou uma única mudança material: `348115` passou de R$ 530 mil para R$ 550 mil. Os demais “changes” históricos são majoritariamente ruído legado de campos contaminados e não foram interpretados como movimento de mercado.
O ranking considera planta original provável de três quartos, QC, funcionalidade da conversão do quintal, cozinha, ventilação, iluminação, conservação, vagas, regularidade e custo de adequação. Fechar o quintal não gera penalidade automática; a funcionalidade real da intervenção é que importa. Áreas anunciadas não foram usadas para preço por m² porque misturam privativa, construída e ampliada. A planta permanece **provável e pendente de documentos** em todos os finalistas.
O novo teto pessoal foi aplicado como filtro eliminatório: **preço anunciado máximo de R$ 750 mil**. Dos 78 ativos, 73 têm três quartos estruturados e preço entre R$ 1 e R$ 750 mil antes das demais quarentenas. O teto não é meta de gasto e não inclui ITBI, registro, mudança, reforma ou regularização; todos esses desembolsos ainda precisam preservar ao menos R$ 50 mil de reserva líquida.
Em 21/07, o usuário avaliou individualmente os 15 finalistas anteriores em escala de 1 a 5. Como esse feedback revelou critérios capazes de alterar também o funil de entrada, a análise foi refeita sobre os **78 anúncios ativos**, não apenas reordenada. Foram relidas descrições e características de todo o universo, reaplicadas quarentenas e duplicatas e revistas as galerias dos sobreviventes mais plausíveis. A coluna “nota” só contém avaliações dadas pelo usuário; “inferida” significa aderência estimada pelas evidências, ainda pendente de validação pessoal.
## Auditoria da primeira foto
As primeiras fotos dos 78 anúncios ativos foram triadas, incluindo a primeira foto de `1401074`. `1301400`, `1346017`, `1346231`, `1349519`, `1351729`, `1352943`, `1356197`, `1362968` e `1366801` exibem “VENDIDO”, “negócio fechado” ou equivalente, embora ainda estejam ativos no portal. Nenhum dos 15 colocados atuais apresenta esse indicativo. O selo indica provável indisponibilidade operacional, não comprova a transação.
A triagem perceptual foi reexecutada com Pillow 12.3.0. Ela recuperou os grupos de republicação já conhecidos; entre os candidatos, `1363572` teve sinal fraco com `1370918` (quatro fotos semelhantes). A divergência de área e a baixa quantidade de correspondências não justificam consolidá-los automaticamente. A similaridade não provocou nova consolidação; a mudança do ranking decorreu do feedback do usuário e da revisão integral do universo ativo.
## Ranking
| # | Anúncio | Nota | QC | Preço | Leitura após análise integral | Principal pendência |
|---:|---|---:|---:|---:|---|---|
| 1 | [1279892](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-rua-f-1279892) | 4/5 | 10 | R$ 595 mil | Boa cozinha semi-integrada à área gourmet; poucas alterações e bom preço | Planta, áreas divergentes e regularização |
| 2 | [1319996](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1319996) | 4/5 | 14 | R$ 649 mil | Cozinha, gourmet, quintal, quartos e banheiros fortes; energia solar é bônus | Reversão futura do closet, cobertura e homologação solar |
| 3 | [1377683](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1377683) | inferida | 11 | R$ 585 mil | Cozinha clara e funcional ligada a gourmet aberto, bom preço e conservação aparente | Usuário avaliar; planta, cobertura, drenagem e ruído |
| 4 | [1371718](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-rua-c-1371718) | inferida | 14 | R$ 650 mil | Cozinha equipada, marcenaria conservada e gourmet aberto sem piscina | Usuário avaliar; sistemas, cobertura e insolação |
| 5 | [1140317](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-5-rua-l-1140317) | inferida | 5 | R$ 599 mil | Cozinha bem equipada e área gourmet aberta integrada ao fluxo da casa | Usuário avaliar; acabamento heterogêneo, cobertura e uma vaga anunciada |
| 6 | [1324014](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-12-rua-k-1324014) | 3/5 | 12 | R$ 649 mil | Gourmet agradável, cozinha razoavelmente espaçosa e boa conservação | Confirmar vaga e ventilação; sanca é incômodo reversível |
| 7 | [1290502](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1290502) | 3/5 | 11 | R$ 600 mil | Reforma, cozinha e banheiros adequados; gourmet pode ser implementado | Planta, cobertura, posição solar, custo do gourmet e preço R$ 600/620 mil |
| 8 | [1401074](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-13-1401074) | inferida | 13 | R$ 720 mil | Casa conservada, cozinha planejada e gourmet aberto ligado a quintal amplo | Usuário avaliar; cuba de vidro, acabamentos externos, documentação dos 110 m² e cobertura |
| 9 | [1345745](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-quadra-qc-12-1345745) | inferida | 12 | R$ 670 mil | Cozinha funcional e gourmet amplo, conectado e sem equipamento aquático | Usuário avaliar; calor, ventilação e regularidade da grande cobertura |
| 10 | [1333569](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-6-1333569) | 3/5 | 6 | R$ 650 mil | Gourmet e banheiros bons | Testar efeito estufa do fechamento em vidro e fluxo interno confuso |
| 11 | [1357311](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-1-rua-a-1357311) | inferida | 1 | R$ 699,9 mil | Cozinha conectada a gourmet grande e iluminado | Usuário avaliar; sinais aparentes de infiltração/desplacamento no teto, QC e preço |
| 12 | [1357912](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-3-1357912) | 3/5 | 3 | R$ 580 mil | Espaços bem aproveitados | Marcenaria básica e intervenções futuras antes da mudança |
| 13 | [1259149](https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-11-1259149) | inferida | 11 | R$ 590 mil | Preço e QC razoáveis, quintal parcialmente aberto | Usuário avaliar; acabamento básico, área anunciada e custo de adequação |
| 14 | [348115](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-rua-c-348115) | inferida | 14 | R$ 550 mil | Preço baixo, três quartos e churrasqueira sem piscina anunciada | Usuário avaliar; anúncio antigo, conservação, coerência dos 120 m² e modernização |
| 15 | [1346118](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-14-1346118) | inferida | 14 | R$ 550 mil | Base simples e barata, sem intervenção aquática anunciada | Usuário avaliar; exige modernização e área diverge entre 63/110 m² |
## Quase entraram
- [1352633](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-9-rua-g-1352633), QC 9, R$ 755 mil: visualmente aderente, mas **excluído do ranking por superar o teto confirmado de R$ 750 mil**.
- `1290512`, anteriormente avaliado em 3/5, ficou inativo em 22/07; a ausência foi registrada sem inferir venda.
- `1353638`, antes entre os quase classificados, ficou inativo em 21/07; a ausência foi registrada sem inferir venda.
- [1041635](https://www.dfimoveis.com.br/imovel/casa-condominio-3-quartos-venda-jardins-mangueiral-brasilia-df-qc-10-1041635), QC 10, R$ 690 mil: piscina e boa localização; é provável duplicata de `1383513` e exige auditoria estrutural maior.
## Quarentenas e limitações
- Vendido na primeira foto: `1301400`, `1346017`, `1346231`, `1349519`, `1351729`, `1352943`, `1356197`, `1362968` e `1366801`.
- Acima do teto de R$ 750 mil: `1352633` (R$ 755 mil) e qualquer outra oferta que ultrapasse o limite, ainda que visualmente aderente.
- Novos inativos em 21/07: `1346001`, `1350155`, `1353638` e `1358463`; ausência da busca não prova venda.
- Novo inativo em 22/07: `1290512`; ausência da busca não prova venda.
- `1201070` possui marcação de permuta na galeria e permanece fora; permuta não foi tratada como prova de venda.
- `1369473` é uma solicitação de compra (“procuro casa”), não uma oferta imobiliária, e foi colocado em quarentena por incompatibilidade de fonte.
- `1350829` e `1358463` anunciam quatro quartos; `1348449` e `1374012` declaram quarto adaptado; `1026522` diverge do campo estruturado ao descrever quatro quartos; `1351663` tem preço estruturado de R$ 570 milhões.
- Grupos de republicações não foram multiplicados, incluindo `1041635`/`1383513`, `1239730`/`1242337`/`1294027`/`1359192`/`1359662`, `1354016`/`1377683` e `1356153`/`1358854`/`1358920`.
- Só visita e documentação confirmam planta original, licenças, estrutura, impermeabilização, insolação, taxas e disponibilidade.
## Próxima ação
Começar a diligência documental por `1279892` e `1319996` e pedir ao usuário uma avaliação visual de `1377683`, `1371718`, `1140317` e `1401074`. Antes de visitar, exigir planta original, QC/condomínio exatos, área privativa versus ampliada e aprovação da cobertura. Piscina, jacuzzi e banheira são incompatibilidades funcionais confirmadas, não bônus; placas solares exigem documentação própria. Para qualquer finalista, calcular custos de compra e adequação além do preço sem violar a reserva mínima.
## Reprodutibilidade
Execute `python reports/mangueiral_top15/analysis.py` para a extração somente leitura. O julgamento fotográfico e a deduplicação final estão documentados aqui.

2
reports/mangueiral_top10/analysis.py → reports/mangueiral_top15/analysis.py

@ -72,7 +72,7 @@ def main() -> int:
l.raw_html_path, COUNT(p.source_url) AS photo_count l.raw_html_path, COUNT(p.source_url) AS photo_count
FROM listings AS l FROM listings AS l
JOIN listing_searches AS s USING (listing_id) JOIN listing_searches AS s USING (listing_id)
LEFT JOIN photos AS p USING (listing_id) LEFT JOIN photos AS p ON p.listing_id=l.listing_id AND p.is_primary_gallery=1
WHERE instr(s.search_url, ?) > 0 WHERE instr(s.search_url, ?) > 0
AND l.inactive_at IS NULL AND l.inactive_at IS NULL
GROUP BY l.listing_id GROUP BY l.listing_id

38
reports/mangueiral_top15/parameters.json

@ -0,0 +1,38 @@
{
"analysis_date": "2026-07-22",
"listing_data_reference_date": "2026-07-22",
"database_path": "dfimoveis_data/dfimoveis.sqlite3",
"database_main_file_sha256": "2d02c4c15f56ae60b1ce8ee1aa00c453e61cb0559d5c429ad7e20cb58802341c",
"database_mode": "read_only_transaction_with_wal",
"search_url_fragment": "/jardins-mangueiral/",
"listing_limit": 150,
"observed_listing_count": 95,
"active_listing_count": 78,
"inactive_listing_count": 17,
"database_crawl_run_count": 22,
"database_listing_count": 162,
"database_observation_count": 638,
"database_photo_record_count": 7457,
"primary_gallery_photo_count": 3521,
"active_mangueiral_primary_gallery_photo_count": 1865,
"active_three_bedroom_at_or_below_price_ceiling_count": 73,
"full_active_universe_reprocessed_after_user_feedback": true,
"full_active_universe_size": 78,
"new_inferred_fit_listing_ids_pending_user_review": ["1377683", "1371718", "1140317", "1401074", "1345745", "1357311", "1259149", "348115", "1346118"],
"ranking_size": 15,
"ranking_unit": "probable_physical_property",
"numeric_score_used": false,
"maximum_tolerated_listing_price_brl": 750000,
"price_ceiling_is_eliminatory": true,
"price_ceiling_excludes_transaction_and_renovation_costs": true,
"user_feedback_date": "2026-07-21",
"user_visual_functional_score_scale": "1_to_5",
"user_visual_functional_scores_by_listing_id": {"1279892": 4, "1350367": 2, "1397378": 1, "1359960": 2, "1319996": 4, "1324014": 3, "1333569": 3, "1359480": 1, "1290502": 3, "1368562": 2, "1202799": 2, "1357912": 3, "1363572": 2, "1290512": 3, "1354257": 2},
"price_per_m2_used": false,
"original_three_bedroom_requires_document_confirmation": true,
"first_photo_active_universe_audit_count": 78,
"visually_unavailable_listing_ids": ["1301400", "1346017", "1346231", "1349519", "1351729", "1352943", "1356197", "1362968", "1366801"],
"source_mismatch_listing_ids": ["1369473"],
"adapted_or_inconsistent_bedroom_listing_ids": ["1026522", "1348449", "1374012"],
"context_documents_reviewed": ["docs/CURRENT_STATE.md", "docs/PROJECT_CONTEXT.md", "docs/LOCATION_CRITERIA.md", "docs/PROPERTY_CRITERIA.md", "docs/FINANCIAL_STRATEGY.md", "docs/SCRAPER_OPERATIONS.md", "docs/DATA_MODEL.md", "docs/ANALYSIS_RULES.md"]
}

6
reports/mangueiral_top10/photo_similarity.py → reports/mangueiral_top15/photo_similarity.py

@ -22,7 +22,7 @@ DATA_ROOT = DB_PATH.parent
def difference_hash(path: Path) -> int | None: def difference_hash(path: Path) -> int | None:
try: try:
with Image.open(path) as image: with Image.open(path) as image:
pixels = list(image.convert("L").resize((9, 8)).getdata()) pixels = list(image.convert("L").resize((9, 8)).get_flattened_data())
except (OSError, UnidentifiedImageError): except (OSError, UnidentifiedImageError):
return None return None
value = 0 value = 0
@ -48,7 +48,7 @@ def main() -> None:
), uncommon AS ( ), uncommon AS (
SELECT sha256 SELECT sha256
FROM photos FROM photos
WHERE sha256 IS NOT NULL WHERE sha256 IS NOT NULL AND is_primary_gallery=1
GROUP BY sha256 GROUP BY sha256
HAVING COUNT(DISTINCT listing_id) <= 5 HAVING COUNT(DISTINCT listing_id) <= 5
) )
@ -56,7 +56,7 @@ def main() -> None:
FROM photos AS p FROM photos AS p
JOIN mangueiral AS m USING (listing_id) JOIN mangueiral AS m USING (listing_id)
JOIN uncommon AS u USING (sha256) JOIN uncommon AS u USING (sha256)
WHERE p.local_path IS NOT NULL WHERE p.local_path IS NOT NULL AND p.is_primary_gallery=1
ORDER BY p.listing_id, p.ordinal ORDER BY p.listing_id, p.ordinal
""" """
).fetchall() ).fetchall()

3
reports/mangueiral_top10/query.sql → reports/mangueiral_top15/query.sql

@ -21,11 +21,12 @@ WHERE instr(s.search_url, '/jardins-mangueiral/') > 0
ORDER BY l.price_brl, l.listing_id ORDER BY l.price_brl, l.listing_id
LIMIT 150; LIMIT 150;
-- Cardinalidade de fotos por anúncio. A contagem inclui material genérico do portal. -- Cardinalidade de fotos da galeria primária por anúncio; recomendações legadas são excluídas.
SELECT p.listing_id, COUNT(*) AS photo_count SELECT p.listing_id, COUNT(*) AS photo_count
FROM photos AS p FROM photos AS p
JOIN listing_searches AS s USING (listing_id) JOIN listing_searches AS s USING (listing_id)
WHERE instr(s.search_url, '/jardins-mangueiral/') > 0 WHERE instr(s.search_url, '/jardins-mangueiral/') > 0
AND p.is_primary_gallery = 1
AND EXISTS ( AND EXISTS (
SELECT 1 FROM listings AS l SELECT 1 FROM listings AS l
WHERE l.listing_id = p.listing_id AND l.inactive_at IS NULL WHERE l.listing_id = p.listing_id AND l.inactive_at IS NULL

16
reports/mangueiral_top15/results.csv

@ -0,0 +1,16 @@
rank,representative_listing_id,user_score,assessment_basis,probable_duplicate_ids,qc,price_brl,area_m2,bedrooms,suites,parking_spaces,original_three_bedroom_status,visual_status,decision
1,1279892,4,user_feedback,,10,595000,68,3,1,1,probable_pending_documents,no_sale_marker,strong_candidate
2,1319996,4,user_feedback,,14,649000,68,3,1,2,probable_pending_documents,no_sale_marker,strong_candidate
3,1377683,,inferred_from_full_database_review,,11,585000,69,3,1,2,probable_pending_documents,no_sale_marker,user_visual_review_then_documents
4,1371718,,inferred_from_full_database_review,,14,650000,100,3,1,2,probable_pending_documents,no_sale_marker,user_visual_review_then_documents
5,1140317,,inferred_from_full_database_review,,5,599000,80,3,1,1,probable_pending_documents,no_sale_marker,user_visual_review_then_documents
6,1324014,3,user_feedback,,12,649000,68,3,1,,probable_pending_documents,no_sale_marker,visit
7,1290502,3,user_feedback,,11,600000,75,3,1,2,probable_pending_documents,no_sale_marker,validate_original_plan_cover_and_price_then_visit
8,1401074,,inferred_from_full_database_review,,13,720000,110,3,1,2,probable_pending_documents,no_sale_marker,user_review_then_documents_and_heat_test
9,1345745,,inferred_from_full_database_review,,12,670000,68,3,1,2,probable_pending_documents,no_sale_marker,user_review_and_heat_test
10,1333569,3,user_feedback,,6,650000,90,3,1,2,probable_pending_documents,no_sale_marker,test_glass_enclosure_heat_then_visit
11,1357311,,inferred_from_full_database_review,,1,699900,68,3,1,2,probable_pending_documents,no_sale_marker,inspect_possible_water_damage
12,1357912,3,user_feedback,,3,580000,110,3,1,2,probable_pending_documents,no_sale_marker,validate_intervention_cost_before_visit
13,1259149,,inferred_from_full_database_review,,11,590000,115,3,1,,probable_pending_documents,no_sale_marker,user_review_and_area_validation
14,348115,,inferred_from_full_database_review,,14,550000,120,3,1,2,probable_pending_documents,no_sale_marker,user_review_and_condition_validation
15,1346118,,inferred_from_full_database_review,,14,550000,110,3,1,2,probable_pending_documents,no_sale_marker,user_review_and_renovation_scope
1 rank representative_listing_id user_score assessment_basis probable_duplicate_ids qc price_brl area_m2 bedrooms suites parking_spaces original_three_bedroom_status visual_status decision
2 1 1279892 4 user_feedback 10 595000 68 3 1 1 probable_pending_documents no_sale_marker strong_candidate
3 2 1319996 4 user_feedback 14 649000 68 3 1 2 probable_pending_documents no_sale_marker strong_candidate
4 3 1377683 inferred_from_full_database_review 11 585000 69 3 1 2 probable_pending_documents no_sale_marker user_visual_review_then_documents
5 4 1371718 inferred_from_full_database_review 14 650000 100 3 1 2 probable_pending_documents no_sale_marker user_visual_review_then_documents
6 5 1140317 inferred_from_full_database_review 5 599000 80 3 1 1 probable_pending_documents no_sale_marker user_visual_review_then_documents
7 6 1324014 3 user_feedback 12 649000 68 3 1 probable_pending_documents no_sale_marker visit
8 7 1290502 3 user_feedback 11 600000 75 3 1 2 probable_pending_documents no_sale_marker validate_original_plan_cover_and_price_then_visit
9 8 1401074 inferred_from_full_database_review 13 720000 110 3 1 2 probable_pending_documents no_sale_marker user_review_then_documents_and_heat_test
10 9 1345745 inferred_from_full_database_review 12 670000 68 3 1 2 probable_pending_documents no_sale_marker user_review_and_heat_test
11 10 1333569 3 user_feedback 6 650000 90 3 1 2 probable_pending_documents no_sale_marker test_glass_enclosure_heat_then_visit
12 11 1357311 inferred_from_full_database_review 1 699900 68 3 1 2 probable_pending_documents no_sale_marker inspect_possible_water_damage
13 12 1357912 3 user_feedback 3 580000 110 3 1 2 probable_pending_documents no_sale_marker validate_intervention_cost_before_visit
14 13 1259149 inferred_from_full_database_review 11 590000 115 3 1 probable_pending_documents no_sale_marker user_review_and_area_validation
15 14 348115 inferred_from_full_database_review 14 550000 120 3 1 2 probable_pending_documents no_sale_marker user_review_and_condition_validation
16 15 1346118 inferred_from_full_database_review 14 550000 110 3 1 2 probable_pending_documents no_sale_marker user_review_and_renovation_scope

2
requirements-dfimoveis.txt

@ -2,7 +2,7 @@ httpx[http2]>=0.27,<1
beautifulsoup4>=4.12,<5 beautifulsoup4>=4.12,<5
lxml>=5,<7 lxml>=5,<7
# Used by the report photo-similarity screening scripts. # Used by the report photo-similarity screening scripts.
Pillow>=10,<13 pillow>=10,<13
# Default browser automation (uses the locally installed Microsoft Edge): # Default browser automation (uses the locally installed Microsoft Edge):
playwright>=1.49,<2 playwright>=1.49,<2
cloudscraper>=1.2,<2 cloudscraper>=1.2,<2

5
run_scraper.ps1

@ -0,0 +1,5 @@
Set-Location E:\forge\python\house-quest
& "C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="E:\forge\python\house-quest\dfimoveis_data\browser-profile"
.\.venv-scraper\Scripts\python.exe .\dfimoveis_scraper.py --search-file .\search_list.txt --output .\dfimoveis_data --attach-chrome

169
scripts/migrate_dfimoveis_schema_v2.py

@ -0,0 +1,169 @@
#!/usr/bin/env python3
"""Add schema-v2 quality metadata without deleting or rewriting source evidence."""
from __future__ import annotations
import argparse
import hashlib
import json
import re
import sqlite3
import sys
from pathlib import Path
from urllib.parse import urlparse
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from dfimoveis_scraper import (
Database,
normalize_creci,
normalize_description_for_hash,
normalize_listing_address,
now_utc,
)
VISUALLY_SOLD = {
"1263868", "1301400", "1346017", "1346231", "1349519",
"1351729", "1352943", "1356197", "1362968", "1366801",
}
def stable_hash(row: sqlite3.Row) -> str:
payload = {
"url": row["url"],
"title": row["title"],
"description": normalize_description_for_hash(row["description"]),
"transaction_type": row["transaction_type"],
"property_type": row["property_type"],
"price_brl": row["price_brl"],
"condominium_brl": row["condominium_brl"],
"iptu_brl": row["iptu_brl"],
"area_m2": row["area_m2"],
"bedrooms": row["bedrooms"],
"suites": row["suites"],
"parking_spaces": row["parking_spaces"],
"address_normalized": normalize_listing_address(row["url"], row["address"]),
"neighborhood_normalized": "Jardins Mangueiral" if "jardins-mangueiral" in row["url"] else None,
"advertiser_name": row["advertiser_name"],
"creci_normalized": normalize_creci(row["creci"]),
"latitude": row["latitude"],
"longitude": row["longitude"],
"published_at": row["published_at"],
}
return hashlib.sha256(json.dumps(payload, sort_keys=True, ensure_ascii=False).encode()).hexdigest()
def upsert_issue(conn: sqlite3.Connection, listing_id: str, code: str, severity: str, evidence: str) -> None:
ts = now_utc()
conn.execute(
"""INSERT INTO data_quality_issues(listing_id,issue_code,severity,evidence,first_seen_at,last_seen_at)
VALUES(?,?,?,?,?,?) ON CONFLICT(listing_id,issue_code) DO UPDATE SET
severity=excluded.severity,evidence=excluded.evidence,last_seen_at=excluded.last_seen_at""",
(listing_id, code, severity, evidence, ts, ts),
)
def migrate(database: Path) -> None:
# Database initialization performs only additive CREATE/ALTER operations.
bootstrap = Database(database)
bootstrap.close()
conn = sqlite3.connect(database)
conn.row_factory = sqlite3.Row
conn.execute("PRAGMA foreign_keys=ON")
with conn:
for row in conn.execute("SELECT * FROM listings"):
normalized_address = normalize_listing_address(row["url"], row["address"])
normalized_neighborhood = "Jardins Mangueiral" if "jardins-mangueiral" in row["url"] else None
normalized_creci = normalize_creci(row["creci"])
conn.execute(
"""UPDATE listings SET address_normalized=?,neighborhood_normalized=?,
creci_normalized=?,raw_content_hash=COALESCE(raw_content_hash,content_hash),
stable_content_hash=? WHERE listing_id=?""",
(normalized_address, normalized_neighborhood, normalized_creci, stable_hash(row), row["listing_id"]),
)
description = (row["description"] or "").lower()
if row["price_brl"] and row["price_brl"] > 20_000_000:
upsert_issue(conn, row["listing_id"], "implausible_price", "high", str(row["price_brl"]))
if row["area_m2"] and row["area_m2"] < 10:
upsert_issue(conn, row["listing_id"], "implausible_area", "high", str(row["area_m2"]))
if "procuro casa para comprar" in description:
upsert_issue(conn, row["listing_id"], "source_mismatch_buyer_request", "high", "description declares a purchase request")
if "quarto adaptado" in description or "escritório adaptado" in description:
upsert_issue(conn, row["listing_id"], "adapted_room", "medium", "description mentions an adapted room")
mismatch_counts: dict[str, int] = {}
for photo in conn.execute("SELECT listing_id,source_url FROM photos"):
owner = re.search(r"/fotos/(\d+)/", urlparse(photo["source_url"]).path, re.I)
is_primary = bool(owner and owner.group(1) == photo["listing_id"])
status = "unreviewed" if is_primary else ("excluded_recommendation" if owner else "excluded_site_asset")
conn.execute(
"UPDATE photos SET discovery_source=?,is_primary_gallery=?,review_status=? WHERE listing_id=? AND source_url=?",
("listing_id_url" if is_primary else "legacy_page_wide_scan", int(is_primary), status, photo["listing_id"], photo["source_url"]),
)
if owner and not is_primary:
mismatch_counts[photo["listing_id"]] = mismatch_counts.get(photo["listing_id"], 0) + 1
for listing_id, count in mismatch_counts.items():
upsert_issue(conn, listing_id, "photo_owner_mismatch", "high", f"{count} legacy photos do not belong to this listing id")
# Confirmed manual exceptions whose structured description is truncated.
manual_issues = [
("1369473", "source_mismatch_buyer_request", "high", "full captured page declares PROCURO CASA PARA COMPRAR"),
("1348449", "adapted_room", "medium", "full description declares an adapted office/room"),
("1374012", "adapted_room", "medium", "full description declares an adapted bedroom"),
("1026522", "bedroom_count_conflict", "medium", "structured three bedrooms conflicts with four bedrooms in description"),
]
for listing_id, code, severity, evidence in manual_issues:
if conn.execute("SELECT 1 FROM listings WHERE listing_id=?", (listing_id,)).fetchone():
upsert_issue(conn, listing_id, code, severity, evidence)
for listing_id in VISUALLY_SOLD:
exists = conn.execute(
"SELECT 1 FROM availability_observations WHERE listing_id=? AND classification='sold_visual' AND source='manual_first_photo_review'",
(listing_id,),
).fetchone()
if not exists and conn.execute("SELECT 1 FROM listings WHERE listing_id=?", (listing_id,)).fetchone():
active = conn.execute("SELECT inactive_at IS NULL FROM listings WHERE listing_id=?", (listing_id,)).fetchone()[0]
conn.execute(
"""INSERT INTO availability_observations
(listing_id,observed_at,portal_active,classification,source,evidence)
VALUES(?,?,?,?,?,?)""",
(listing_id, now_utc(), active, "sold_visual", "manual_first_photo_review", "VENDIDO or equivalent marker on first photo"),
)
latest_run = conn.execute("SELECT * FROM crawl_runs ORDER BY id DESC LIMIT 1").fetchone()
if latest_run and latest_run["searches_json"] == "[]" and latest_run["finished_at"]:
searches = [
row[0] for row in conn.execute(
"""SELECT DISTINCT search_url FROM listing_searches
WHERE last_seen_at BETWEEN ? AND ? ORDER BY search_url""",
(latest_run["started_at"], latest_run["finished_at"]),
)
]
stats = json.loads(latest_run["stats_json"] or "{}")
conn.execute(
"""UPDATE crawl_runs SET searches_json=?,config_json=?,scope_complete=? WHERE id=?""",
(json.dumps(searches, ensure_ascii=False), json.dumps({"legacy_backfill": True}),
int(latest_run["status"] == "ok" and stats.get("details_failed", 0) == 0), latest_run["id"]),
)
result = conn.execute("PRAGMA quick_check").fetchone()[0]
counts = {
"listings": conn.execute("SELECT count(*) FROM listings").fetchone()[0],
"history": conn.execute("SELECT count(*) FROM listing_history").fetchone()[0],
"photos": conn.execute("SELECT count(*) FROM photos").fetchone()[0],
"non_gallery_photos": conn.execute("SELECT count(*) FROM photos WHERE is_primary_gallery=0").fetchone()[0],
"quality_issues": conn.execute("SELECT count(*) FROM data_quality_issues").fetchone()[0],
"availability_observations": conn.execute("SELECT count(*) FROM availability_observations").fetchone()[0],
}
conn.close()
print(json.dumps({"quick_check": result, **counts}, ensure_ascii=False, indent=2))
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--database", default="dfimoveis_data/dfimoveis.sqlite3")
args = parser.parse_args()
migrate(Path(args.database).resolve())
if __name__ == "__main__":
main()

1
search_list.txt

@ -1,3 +1,2 @@
# One complete DFImoveis search URL per line # One complete DFImoveis search URL per line
https://www.dfimoveis.com.br/venda/df/brasilia/jardins-mangueiral/casa/3,4-quartos https://www.dfimoveis.com.br/venda/df/brasilia/jardins-mangueiral/casa/3,4-quartos
https://www.dfimoveis.com.br/venda/df/cruzeiro/novo/apartamento/3-quartos

64
tests/test_dfimoveis_schema_v2.py

@ -0,0 +1,64 @@
import tempfile
import unittest
from pathlib import Path
from dfimoveis_scraper import Database, Parser, normalize_creci, normalize_listing_address
class SchemaV2Tests(unittest.TestCase):
def test_normalization(self):
self.assertEqual(normalize_creci("26.903 Ver mais qualquer texto"), "26903")
self.assertEqual(
normalize_listing_address(
"https://www.dfimoveis.com.br/imovel/casa-venda-jardins-mangueiral-qc-13-rua-g-123",
"texto contaminado " * 100,
),
"Jardins Mangueiral - QC 13 - Rua G",
)
def test_parser_rejects_recommendation_photos(self):
page = """
<html><head><meta property="og:title" content="Casa 3 quartos"></head><body>
<img src="https://img.dfimoveis.com.br/fotos/1401074/a.webp">
<img src="https://img.dfimoveis.com.br/fotos/999999/b.webp">
</body></html>
"""
data, photos = Parser.detail(
"https://www.dfimoveis.com.br/imovel/casa-3-quartos-venda-jardins-mangueiral-qc-13-1401074",
page,
)
self.assertEqual(photos, ["https://img.dfimoveis.com.br/fotos/1401074/a.webp"])
self.assertEqual(data["creci_normalized"], None)
self.assertEqual(data["address_normalized"], "Jardins Mangueiral - QC 13")
def test_schema_migration_is_additive(self):
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "test.sqlite3"
db = Database(path)
columns = {row[1] for row in db.conn.execute("PRAGMA table_info(listings)")}
self.assertIn("stable_content_hash", columns)
self.assertIn("creci_normalized", columns)
self.assertTrue(db.conn.execute("SELECT 1 FROM sqlite_master WHERE name='data_quality_issues'").fetchone())
db.close()
def test_raw_ui_noise_does_not_create_material_change(self):
page = '<meta property="og:title" content="Casa"><img src="https://img.dfimoveis.com.br/fotos/123/a.webp">'
url = "https://www.dfimoveis.com.br/imovel/casa-venda-jardins-mangueiral-qc-13-123"
data, _ = Parser.detail(url, page)
with tempfile.TemporaryDirectory() as tmp:
db = Database(Path(tmp) / "test.sqlite3")
run_id = db.start_run([url], {"test": True})
self.assertTrue(db.upsert_listing(data, url, run_id))
data["creci"] = "texto instável da interface"
data["address"] = "outro bloco contaminado"
data["raw_content_hash"] = "different-raw-hash"
self.assertFalse(db.upsert_listing(data, url, run_id))
self.assertEqual(db.conn.execute("SELECT count(*) FROM listing_history").fetchone()[0], 1)
self.assertEqual(db.conn.execute("SELECT count(*) FROM listing_change_events").fetchone()[0], 1)
self.assertEqual(db.mark_inactive(url, set(), "9999-01-01T00:00:00+00:00"), 1)
self.assertEqual(db.mark_inactive(url, set(), "9999-01-01T00:00:00+00:00"), 0)
db.close()
if __name__ == "__main__":
unittest.main()
Loading…
Cancel
Save