diff --git a/.env.example b/.env.example index 3ef6c57..8165ec5 100644 --- a/.env.example +++ b/.env.example @@ -2,3 +2,4 @@ HA_WEBHOOK_URL= DB_PATH=/data/huizenbot.db +APP_ENV=dev diff --git a/shell.nix b/shell.nix index 4948499..f421e15 100644 --- a/shell.nix +++ b/shell.nix @@ -1,10 +1,11 @@ -{ pkgs ? import {} }: +{ pkgs ? import { config.allowUnfree = true; } }: pkgs.mkShell { packages = [ (pkgs.python3.withPackages (ps: with ps; [ httpx beautifulsoup4 + flask lxml ])) pkgs.claude-code @@ -15,7 +16,6 @@ pkgs.mkShell { set -a source .env set +a - echo ".env geladen" fi ''; } diff --git a/src/adapters/ssr.py b/src/adapters/ssr.py index 9793ec9..3b4bfda 100644 --- a/src/adapters/ssr.py +++ b/src/adapters/ssr.py @@ -12,7 +12,7 @@ import time import httpx from bs4 import BeautifulSoup -import config +from config import * from huizenbot import RawListing log = logging.getLogger("huizenbot.ssr") @@ -160,6 +160,8 @@ def fetch_realworks(base_url: str, makelaar: str) -> list[RawListing]: slaapkamers=int(kk["slaapkamers"]) if kk.get("slaapkamers") else None, energielabel=kk.get("energielabel"), )) + if APP_ENV == "dev": + break except Exception as e: log.warning("%s: parse fout: %s", makelaar, e) @@ -292,6 +294,9 @@ def fetch_dewittegarantiemakelaars() -> list[RawListing]: bouwjaar=int(bouwjaar) if bouwjaar else None, hero_image_url=hero, )) + if APP_ENV == "dev": + break + except Exception as e: log.warning("dewitte: parse fout: %s", e) @@ -415,6 +420,8 @@ def fetch_wassenaar() -> list[RawListing]: slaapkamers=int(kk["slaapkamers"]) if kk.get("slaapkamers") else None, energielabel=kk.get("energielabel"), )) + if APP_ENV == "dev": + break except Exception as e: log.warning("wassenaar: parse fout: %s", e) @@ -597,6 +604,8 @@ def fetch_dens() -> list[RawListing]: slaapkamers=int(detail_data["slaapkamers"]) if detail_data.get("slaapkamers") else None, energielabel=detail_data.get("energielabel"), )) + if APP_ENV == "dev": + break except Exception as e: log.warning("dens: parse fout: %s", e) @@ -723,6 +732,8 @@ def fetch_3dmakelaars() -> list[RawListing]: slaapkamers=detail_data.get("slaapkamers"), hero_image_url=hero, )) + if APP_ENV == "dev": + break except Exception as e: log.warning("3dmakelaars: parse fout: %s", e) @@ -845,6 +856,9 @@ def fetch_dupont() -> list[RawListing]: slaapkamers=int(detail_data["slaapkamers"]) if detail_data.get("slaapkamers") else None, energielabel=detail_data.get("energielabel"), )) + if APP_ENV == "dev": + break + except Exception as e: log.warning("dupont: parse fout: %s", e) diff --git a/src/config.py b/src/config.py index e55b371..b5959d0 100644 --- a/src/config.py +++ b/src/config.py @@ -17,3 +17,6 @@ DB_PATH = os.environ.get("DB_PATH", "/data/huizenbot.db") FIETS_SNELHEID_FACTOR = 1.27 MAX_PRICE = 300_000 + +APP_ENV = os.environ.get("APP_ENV", "dev") + diff --git a/src/views/index.html b/src/views/index.html new file mode 100644 index 0000000..4f01130 --- /dev/null +++ b/src/views/index.html @@ -0,0 +1,630 @@ + + + + + +Huizenbot + + + + + + +
+

Huizenbot

+ +
+ +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + +
+
+ + +
+ +
+ +
+
Geen woningen gevonden met deze filters.
+ + + + diff --git a/src/web.py b/src/web.py new file mode 100644 index 0000000..e2f9b74 --- /dev/null +++ b/src/web.py @@ -0,0 +1,60 @@ +""" +web.py — huizenbot web interface +Single route: query SQLite, SSR listings into index.html template. +""" +import json +import sqlite3 +import os +from flask import Flask, render_template, g + +DB_PATH = os.environ.get("DB_PATH", "/data/huizenbot.db") + +app = Flask(__name__) + + +def get_db(): + if "db" not in g: + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + g.db = conn + return g.db + + +@app.teardown_appcontext +def close_db(e=None): + db = g.pop("db", None) + if db is not None: + db.close() + + +@app.route("/") +def index(): + conn = get_db() + rows = conn.execute(""" + SELECT + id, url, source_makelaar, first_seen, last_seen, datum_aanmelding, + status, adres, postcode, stad, + prijs, woningtype, woonoppervlak, perceeloppervlak, + kamers, slaapkamers, bouwjaar, energielabel, + hero_image_url, + fiets_mark, fiets_michelle, ov_mark, ov_michelle, + extra + FROM woningen + WHERE status = 'beschikbaar' + ORDER BY first_seen DESC + """).fetchall() + + listings = [] + for row in rows: + d = dict(row) + try: + d["extra"] = json.loads(d["extra"]) if d["extra"] else {} + except Exception: + d["extra"] = {} + listings.append(d) + + return render_template("src/views/index.html", listings_json=json.dumps(listings, ensure_ascii=False)) + + +if __name__ == "__main__": + app.run(debug=True, host="0.0.0.0", port=5000)