diff --git a/regen_map.py b/regen_map.py
deleted file mode 100644
index 0d8f6f3..0000000
--- a/regen_map.py
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/usr/bin/env python3
-"""
-Přegeneruje mapu z již stažených dat (byty_sreality.json).
-Doplní chybějící plochy ze Sreality API, opraví URL, aplikuje filtry.
-"""
-from __future__ import annotations
-
-import json
-import time
-import urllib.request
-from pathlib import Path
-
-from scrape_and_map import (
- generate_map, format_price, MIN_AREA, HEADERS, DETAIL_API
-)
-
-
-def api_get(url: str) -> dict:
- req = urllib.request.Request(url, headers=HEADERS)
- with urllib.request.urlopen(req, timeout=30) as resp:
- return json.loads(resp.read().decode("utf-8"))
-
-
-def fix_sreality_url(estate: dict) -> str:
- """Fix the Sreality URL to include disposition segment (only if missing)."""
- disp = estate.get("disposition", "")
- slug_map = {
- "1+kk": "1+kk", "1+1": "1+1", "2+kk": "2+kk", "2+1": "2+1",
- "3+kk": "3+kk", "3+1": "3+1", "4+kk": "4+kk", "4+1": "4+1",
- "5+kk": "5+kk", "5+1": "5+1", "6+": "6-a-vice", "Atypický": "atypicky",
- }
- slug = slug_map.get(disp, "byt")
- old_url = estate.get("url", "")
- parts = old_url.split("/")
- try:
- byt_idx = parts.index("byt")
- # Only insert if disposition slug is not already there
- if byt_idx + 1 < len(parts) and parts[byt_idx + 1] == slug:
- return old_url # already correct
- parts.insert(byt_idx + 1, slug)
- return "/".join(parts)
- except ValueError:
- return old_url
-
-
-def fetch_area(hash_id: int) -> int | None:
- """Fetch area from detail API."""
- try:
- url = DETAIL_API.format(hash_id)
- detail = api_get(url)
- for item in detail.get("items", []):
- name = item.get("name", "")
- if "žitná ploch" in name or "zitna ploch" in name.lower():
- return int(item["value"])
- except Exception:
- pass
- return None
-
-
-def main():
- json_path = Path("byty_sreality.json")
- if not json_path.exists():
- print("Soubor byty_sreality.json nenalezen. Nejprve spusť scrape_and_map.py")
- return
-
- estates = json.loads(json_path.read_text(encoding="utf-8"))
- print(f"Načteno {len(estates)} bytů z byty_sreality.json")
-
- # Step 1: Fetch missing areas
- missing_area = [e for e in estates if e.get("area") is None]
- print(f"Doplňuji plochu u {len(missing_area)} bytů...")
-
- for i, e in enumerate(missing_area):
- time.sleep(0.3)
- area = fetch_area(e["hash_id"])
- if area is not None:
- e["area"] = area
- if (i + 1) % 50 == 0:
- print(f" {i + 1}/{len(missing_area)} ...")
-
- # Count results
- with_area = sum(1 for e in estates if e.get("area") is not None)
- print(f"Plocha doplněna: {with_area}/{len(estates)}")
-
- # Step 2: Fix URLs
- for e in estates:
- e["url"] = fix_sreality_url(e)
-
- # Step 3: Filter by min area
- filtered = []
- excluded = 0
- for e in estates:
- area = e.get("area")
- if area is not None and area < MIN_AREA:
- excluded += 1
- continue
- filtered.append(e)
-
- print(f"Vyloučeno (< {MIN_AREA} m²): {excluded}")
- print(f"Zbývá: {len(filtered)} bytů")
-
- # Save updated data
- filtered_path = Path("byty_sreality.json")
- filtered_path.write_text(
- json.dumps(filtered, ensure_ascii=False, indent=2),
- encoding="utf-8",
- )
-
- # Generate map
- generate_map(filtered)
-
-
-if __name__ == "__main__":
- main()
diff --git a/scrape_and_map.py b/scrape_and_map.py
index 3594b09..c129bb8 100644
--- a/scrape_and_map.py
+++ b/scrape_and_map.py
@@ -13,7 +13,7 @@ import math
import time
import urllib.request
import urllib.parse
-from datetime import datetime
+from datetime import datetime, timedelta
from pathlib import Path
from scraper_stats import write_stats
@@ -448,9 +448,13 @@ def generate_map(estates: list[dict], output_path: str = "mapa_bytu.html"):
price_legend_items += (
'
'
- ''
- 'Nové (z dnešního scrapu) — větší
'
+ ''
+ ''
+ 'NEW'
+ ''
+ 'Nové (≤ 1 den)'
)
markers_js = ""
@@ -476,7 +480,9 @@ def generate_map(estates: list[dict], output_path: str = "mapa_bytu.html"):
first_seen = e.get("first_seen", "")
last_changed = e.get("last_changed", "")
- is_new = first_seen == datetime.now().strftime("%Y-%m-%d")
+ today = datetime.now().strftime("%Y-%m-%d")
+ yesterday = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
+ is_new = first_seen in (today, yesterday)
new_badge = (
'
@@ -784,19 +787,28 @@ function addMarker(lat, lon, color, popup, hashId, firstSeen, lastChanged) {{
function addNewMarker(lat, lon, color, popup, hashId, firstSeen, lastChanged) {{
var marker = L.circleMarker([lat, lon], {{
- radius: 12,
+ radius: 8,
fillColor: color,
- color: color,
- weight: 4,
- opacity: 0.35,
- fillOpacity: 0.95,
+ color: '#fff',
+ weight: 2,
+ opacity: 1,
+ fillOpacity: 0.85,
}}).bindPopup(popup);
marker._data = {{ lat: lat, lon: lon, color: color, hashId: hashId, isNew: true, firstSeen: firstSeen || '', lastChanged: lastChanged || '' }};
allMarkers.push(marker);
marker.addTo(map);
- marker.on('add', function() {{
- if (marker._path) marker._path.classList.add('marker-new');
+ var badge = L.marker([lat, lon], {{
+ icon: L.divIcon({{
+ className: 'new-badge-icon',
+ html: 'NEW',
+ iconSize: [32, 14],
+ iconAnchor: [-6, 7],
+ }}),
+ interactive: false,
+ pane: 'markerPane',
}});
+ badge.addTo(map);
+ marker._newBadge = badge;
}}
function heartIcon(color) {{
@@ -899,6 +911,7 @@ function applyMarkerStyle(marker, status) {{
}} else {{
if (status === 'fav') {{
removeRejectStrike(marker);
+ if (marker._newBadge && map.hasLayer(marker._newBadge)) map.removeLayer(marker._newBadge);
if (!marker._data._origCircle) marker._data._origCircle = true;
var popup = marker.getPopup();
var popupContent = popup ? popup.getContent() : '';
@@ -922,6 +935,7 @@ function applyMarkerStyle(marker, status) {{
}}
// Add strikethrough line over the marker
addRejectStrike(marker);
+ if (marker._newBadge && map.hasLayer(marker._newBadge)) map.removeLayer(marker._newBadge);
}} else {{
if (marker._data._origCircle && !(marker instanceof L.CircleMarker)) {{
revertToCircle(marker, {{ radius: 8, fillColor: marker._data.color, color: '#fff', weight: 2, fillOpacity: 0.85 }});
@@ -934,6 +948,7 @@ function applyMarkerStyle(marker, status) {{
}}
if (marker._path) marker._path.classList.remove('marker-rejected');
removeRejectStrike(marker);
+ if (marker._newBadge && !map.hasLayer(marker._newBadge)) marker._newBadge.addTo(map);
}}
}}
}}
@@ -1089,7 +1104,9 @@ map.on('popupopen', function(e) {{
// ── Filters ────────────────────────────────────────────────────
function applyFilters() {{
var minFloor = parseInt(document.getElementById('min-floor').value);
- var maxPrice = parseInt(document.getElementById('max-price').value);
+ var maxPriceEl = document.getElementById('max-price');
+ var maxPrice = parseInt(maxPriceEl.value) || 14000000;
+ if (maxPrice > 14000000) {{ maxPrice = 14000000; maxPriceEl.value = 14000000; }}
var hideRejected = document.getElementById('hide-rejected').checked;
var daysFilter = parseInt(document.getElementById('days-filter').value) || 0;
var ratings = loadRatings();
@@ -1130,10 +1147,12 @@ function applyFilters() {{
visible++;
// Show strike line if rejected and visible
if (m._rejectStrike && !map.hasLayer(m._rejectStrike)) m._rejectStrike.addTo(map);
+ if (m._newBadge && !map.hasLayer(m._newBadge)) m._newBadge.addTo(map);
}} else {{
if (map.hasLayer(m)) map.removeLayer(m);
// Hide strike line when marker hidden
if (m._rejectStrike && map.hasLayer(m._rejectStrike)) map.removeLayer(m._rejectStrike);
+ if (m._newBadge && map.hasLayer(m._newBadge)) map.removeLayer(m._newBadge);
}}
}});
diff --git a/scrape_idnes.py b/scrape_idnes.py
index 820ded6..88f17ff 100644
--- a/scrape_idnes.py
+++ b/scrape_idnes.py
@@ -15,7 +15,6 @@ import re
import time
import urllib.request
import urllib.parse
-from html.parser import HTMLParser
from pathlib import Path
from scraper_stats import write_stats