CzechAI Reality Data Logic — Kanonická pravidla¶
Zdroj pravdy pro MASTER_LIVE.db. Všechny služby (scraper, pricer, hunter, dashboard, reporty) se drží těchto pravidel. Při rozporu mezi kódem a tímto dokumentem → kód je bug, ne dokument.
Verze: 1.0 Datum: 2026-04-05 DB: /opt/reality-pipeline/data/MASTER_LIVE.db (server 179) Udržuje: data_refiner.py (běží on-demand, obnovuje refined_* sloupce)
1. Kanonické hodnoty sloupců¶
1.1 property_type (vždy lowercase)¶
| Hodnota | Popis | Aliases přijaté refinerem |
|---|---|---|
byt | Byt v osobním, družstevním, družstevním nebo společném vlastnictví | BYT, byt |
dum | Rodinný dům, vila, chata, chalupa | DUM, dum, DOM |
pozemek | Stavební, zemědělský, lesní, zahrada (>100 m² plochy) | POZEMEK, pozemek, POZEMK, POZ |
komercni | Kancelář, obchod, restaurace, sklad, výrobna | KOMERCNI, KOMERCNI-NEMOVITOST, komercni |
garaz | Garáž, garážové stání, malý objekt | GARAZ, MALY-OBJEKT-NEBO-GARAZ |
ostatni | Chatka, mobilheim, modulární, nezařaditelné | OSTATNI, MODULARNI, UNK, unknown, reality |
Pokud prázdné: refiner se pokusí odvodit z titulku/popisu (fn infer_ptype_from_title). Když se nepodaří → zůstane prázdné → automaticky padá do refined_status='junk'.
1.2 transaction_type (vždy lowercase)¶
| Hodnota | Popis |
|---|---|
prodej | Prodej nemovitosti (price_asking = kupní cena) |
pronajem | Nájem (price_asking = měsíční nájem v Kč) |
drazba | Dražba, exekutorská aukce, výběrové řízení (cena = vyvolávací) |
Aliases: PRODEJ/prodej, PRONAJEM/pronajem, DRAZBA/drazba — refiner mapuje na lowercase.
1.3 refined_status (nové, povinné)¶
| Hodnota | Význam | Hunter používá? |
|---|---|---|
ok | Záznam prošel všemi kontrolami bez jediné úpravy | ✅ ano |
fixed | Refiner opravil ≥1 field (typ normalized, amenity extracted, city trimmed…) | ✅ ano |
suspect | Záznam má podezřelou hodnotu (extrémní plocha, extrémní cena) — neumazáno, ale flag | ⚠️ jen když uživatel výslovně povolí |
junk | Nepoužitelné (prázdné everything, no_price_no_area, area_too_small) | ❌ nikdy |
duplicate | Deduplikace: existuje jiný záznam se stejným title+area+price+city a novějším updated_at | ❌ nikdy |
Hunter filter ze zákona: sql WHERE refined_status IS NULL OR refined_status NOT IN ('junk', 'duplicate') (NULL znamená "ještě nebyl refined" — toleruje se, ale refiner by měl proběhnout pravidelně.)
1.4 refined_flags (JSON array)¶
Seznam tagů co refiner udělal. Pomáhá při debuggingu a auditingu.
Příklady tagů: - pt_normalized — property_type normalizován (BYT → byt) - pt_inferred — property_type odvozen z titulku (např. "prodej bytu 3+kk" → byt) - tt_normalized — transaction_type normalizován - city_trimmed — město mělo mezery/case problém - rooms_extracted — rooms extrahovány regexem z titulku/popisu - floor_extracted — podlaží extrahováno - year_extracted — rok výstavby extrahován - amenities_extracted — alespoň 1 amenita (výtah, balkon, garáž…) detekována - novostavba — slovo "novostavba" v textu - auction_detected_text — nalezen auction keyword ("dražba", "exekuce", "insolvence") - reclassified_as_rent — byl prodej ale price < 500k → přeznačen na pronajem - area_divide10 — area fixed (1125 m² → 112.5 m²) - area_too_large / area_too_small_byt / area_too_small_dum — suspect areas - is_active_null_fixed — is_active bylo NULL, nastaveno na 0 - empty_everything — žádný title, žádný desc, žádný image → junk - no_price_no_area → junk
2. Validační rozsahy (per property_type)¶
2.1 Plocha (area, m²)¶
| Type | Min | Max | Poznámka |
|---|---|---|---|
byt | 15 | 400 | Nad 1000 → refiner dělí 10 (digit bug fix). 400-1000 → suspect. |
dum | 30 | 1500 | Nad 5000 → refiner dělí 10. |
pozemek | 100 | 50,000 | (Pozemky mají široký rozsah) |
komercni | 15 | 5,000 | — |
garaz | 5 | 50 | — |
2.2 Cena (price_asking, Kč)¶
Prodej: | Type | Min | Max | Akce pokud mimo | |---|---:|---:|---| | byt | 500,000 | 100,000,000 | <500k → reclassify jako pronajem. >200M → suspect. | | dum | 800,000 | 200,000,000 | — | | pozemek | 50,000 | 100,000,000 | — | | komercni | 500,000 | 500,000,000 | — |
Pronájem: | Type | Min | Max (Kč/měsíc) | |---|---:|---:| | byt | 5,000 | 200,000 | | dum | 10,000 | 500,000 | | komercni | 3,000 | 1,000,000 |
Nula nebo záporná cena: pokud i area = 0 → junk. Jinak suspect.
3. Data Quality Pipeline (pořadí operací)¶
┌───────────────────────────────────────────────────────┐ │ 1. SCRAPER (134, various PM2 jobs) │ │ → raw rows → MASTER_LIVE.db │ │ Zodpovědnost: získat title, url, price, area, │ │ city, property_type, image_url, description │ ├───────────────────────────────────────────────────────┤ │ 2. DATA_WATCHDOG (179, PM2 #148) │ │ Každých 5 min: pokud >1000 unpriced → spouští │ │ mass_price_v2.py (s BATCH TRANSAKCEMI!) │ ├───────────────────────────────────────────────────────┤ │ 3. MASS_PRICE_V2 (triggered by watchdog) │ │ → V15f API → fills market_price, price_se2 │ │ KRITICKÉ: BEGIN/COMMIT per 500 rows, NIKDY │ │ isolation_level=None │ ├───────────────────────────────────────────────────────┤ │ 4. DATA_REFINER (on-demand, ~25s) │ │ → normalizes types, extracts rooms/floor/year, │ │ sets refined_status, refined_flags │ │ Spouštění: ručně nebo z cronu (1×/den doporučeno) │ ├───────────────────────────────────────────────────────┤ │ 5. HUNTER (179, PM2 #149 + #151 daemon) │ │ → čte JEN refined_status IN (NULL, 'ok', 'fixed') │ │ → V15f valuace → Claude Sonnet 4.6 verdikt │ │ → link validation → report HTML │ └───────────────────────────────────────────────────────┘
Pravidla konkurence: - Hunter čte only, nikdy neaktualizuje reality_master - Scraper píše přes vlastní connection s WAL + BEGIN/COMMIT - Data-refiner získá exkluzivní lock na DB přes batch transakce - Nikdo nepoužívá isolation_level=None (autocommit mode) — způsobuje corruption
4. Extrakční regexy (data_refiner.py)¶
4.1 Rooms (místnosti)¶
regex \b(?:(\d+)\s*\+\s*(kk|KK|1|2))\b|\b(garsoni[eé]ra|garsonka|atelier)\b Formát výstupu: 1+kk, 2+kk, 3+1, 4+1, 1+kk (garsonka), atelier
4.2 Floor (podlaží)¶
regex \b(p[řr][íi]zem[íi]|(\d+)\s*\.\s*(?:patro|NP|podla[žz][íi])|mezonet|sklep|suter[éen]n|podkrov[íi])\b Výstup: 0 (přízemí), -1 (sklep/suterén), 1..N, mezonet, podkroví
4.3 Year (rok výstavby)¶
regex \b(1[89]\d{2}|20[0-3]\d)\b Vezme nejvyšší rok v rozmezí 1850-2035.
4.4 Amenity keywords (case-insensitive substring)¶
| Flag | Keywords |
|---|---|
elevator | výtah, vytah |
balcony | balkon, balkón |
terrace | terasa, teras |
cellar | sklep, sklad |
parking | parkován, parkovan, parkovací, parkoviste |
garage | garáž, garaz |
garden | zahrada, zahrádk |
mezonet | mezonet |
4.5 Auction signals¶
dražba, drazba, výběrové řízení, vyberove rizeni, insolven, exekuc, konkurs, nucený prodej → Nastaví is_auction = 1 + flag auction_detected_text.
5. Deduplication rules¶
Primární klíč: url (UNIQUE constraint v DB).
Sekundární fingerprint: LOWER(title) | area | price_asking | LOWER(city) — pokud existují 2+ záznamy se stejným fingerprintem, ponecháme ten s nejnovějším updated_at (ostatní dostanou refined_status='duplicate' a is_active=0).
Proč NE hash celého řádku: scraper může znovu naskenovat stejný inzerát s mírně jinými fields (title přepis, rooms přidané) a vytvořit technicky jiný hash, ale sémanticky stejný inzerát.
6. Hunter Agent filter (ZÁVAZNÉ)¶
Každá hunter query MUSÍ obsahovat tyto podmínky:
sql WHERE is_active = 1 AND transaction_type = 'prodej' -- canonical lowercase AND (is_auction = 0 OR is_auction IS NULL) AND (refined_status IS NULL OR refined_status NOT IN ('junk', 'duplicate')) AND price_asking > 0 AND area > 0 AND LENGTH(COALESCE(title, '')) >= 10 AND url NOT LIKE '%bazos%' -- low-quality portal
Plus per-type area limits (viz sekce 2.1).
Pro property_types filter použít IN list s kanonickými lowercase hodnotami: sql AND property_type IN ('byt', 'dum', 'pozemek', 'komercni')
7. Monitoring & SLA¶
7.1 Data quality metriky (aim)¶
| Metrika | Cíl | Aktuální (2026-04-05) |
|---|---|---|
% refined_status = junk | < 20 % | 34 % ⚠️ |
% prázdný description | < 50 % | 94 % 🚨 |
% prázdný city | < 5 % | 30 % ⚠️ |
% prázdný image_url | < 20 % | 34 % ⚠️ |
% prázdný gps_lat | < 30 % | 52 % ⚠️ |
| Duplicity (fingerprint) | < 1 % | 1.4 % ✅ |
Akce při překročení: zpátky do scraperu, zjistit proč chybí data (regresní bug).
7.2 Alerting¶
data-watchdogmá pravidelný loop, mohl by reportovat quality metriky do Prometheus/Telegram.hunter-daemonlogy summary každý cyklus (už existuje).
8. Zakázáno 🚫¶
- Nikdy nepoužívej
isolation_level=Nonena MASTER_LIVE.db — způsobuje corruption při concurrent WAL access. Vždy BEGIN/COMMIT batche. - Nikdy nedělej
DELETE FROM reality_master— místo tohois_active = 0+refined_status='duplicate'/'junk'. - Nikdy nepiš do DB z hunter-agent — hunter je read-only.
- Nikdy neinvaliduj refined_status bez spuštění refineru znovu.
- Nikdy nemíchej property_type uppercase s lowercase — kanonická forma je lowercase only.
- Nikdy nepřidávej nové property_type hodnoty bez aktualizace tohoto dokumentu a
PTYPE_MAPvdata_refiner.py. - Nikdy nefiltruj pouze
is_active=1bez takérefined_status NOT IN ('junk','duplicate')— junk řádky mohou mít is_active=1 z legacy stavu.
9. Kde co leží¶
| Komponent | Cesta | Server |
|---|---|---|
| DB | /opt/reality-pipeline/data/MASTER_LIVE.db | 179 |
| Scraper | /opt/reality-scraper-next/NOVY_SCRAPER_KURVA/ + /opt/reality-pipeline/ | 134 |
| Pricer | /opt/reality-pipeline/mass_price_v2.py (fixed 2026-04-05) | 179 |
| Watchdog | /opt/reality-pipeline/data_watchdog.py (PM2 #148) | 179 |
| Refiner | /opt/hunter-agent/data_refiner.py (on-demand) | 179 |
| Hunter API | /opt/hunter-agent/{hunter,pipeline,llm_router,valuator,enrichers,deep_enrichers}.py | 179 |
| Hunter daemon | /opt/hunter-agent/daemon.py (PM2 #151) | 179 |
| Dashboard | /opt/hunter-agent/dashboard.html | 179 |
| DATA_LOGIC.md | /opt/hunter-agent/DATA_LOGIC.md (tento dokument) | 179 |
10. Jak spustit refiner (cheatsheet)¶
```bash
Stop writers¶
ssh root@46.224.121.179 "pm2 stop hunter-agent hunter-daemon data-watchdog && pkill -9 -f mass_price_v2"
Backup¶
ssh root@46.224.121.179 "cp /opt/reality-pipeline/data/MASTER_LIVE.db /opt/reality-pipeline/data/MASTER_LIVE.db.pre_refine_$(date +%Y%m%d)"
Run refiner (~25s)¶
ssh root@46.224.121.179 "cd /opt/hunter-agent && python3 data_refiner.py"
Restart writers¶
ssh root@46.224.121.179 "pm2 start hunter-agent hunter-daemon data-watchdog" ```
Cron doporučený: každou noc v 3:00 (po scraper cyklu): cron 0 3 * * * root cd /opt/hunter-agent && python3 data_refiner.py >> /var/log/data_refiner.log 2>&1
11. Changelog¶
- 2026-04-05 v1.0 — First version. Created after massive data audit revealed:
- 94% missing description
- 33k missing property_type
- 5.6k rentals mislabeled as sales
- 713 bytů with area > 1000 m²
- 3675 duplicates
- MASTER_LIVE.db corrupted twice by
mass_price_v2.py(autocommit bug) - Fixed: added
refined_status,refined_flags, canonical property_type/transaction_type, regex extractors, dedup, proper transactions in pricer.