fix: round €/m² in Telegram, drop "Bilder nachladen" admin button, fix lightbox visibility
- notifications: round sqm_price to whole € in Telegram match messages
(was emitting raw float like "12.345614 €/m²").
- wohnungen: remove the admin-only "Bilder nachladen (N)" button. It
flickered into view whenever a freshly-scraped flat was still in
pending state, which was effectively random from the user's point of
view, and the manual backfill it triggered isn't needed anymore — new
flats are auto-enriched at scrape time. Also drops the dead helpers
it was the sole caller of: enrichment.kick_backfill,
enrichment._backfill_runner, db.flats_needing_enrichment,
db.enrichment_counts.
- lightbox: the modal didn't appear because Tailwind's Play CDN injects
its own .hidden { display: none } rule at runtime, which kept fighting
our class toggle. Switch the show/hide to inline style.display so no
external stylesheet can mask it. Single-class .lightbox now only owns
the layout — the initial-hidden state is on the element via
style="display:none".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
787f848aba
commit
ee7ba6c6ff
8 changed files with 14 additions and 76 deletions
26
web/db.py
26
web/db.py
|
|
@ -596,32 +596,6 @@ def set_flat_enrichment(flat_id: str, status: str,
|
|||
)
|
||||
|
||||
|
||||
def flats_needing_enrichment(limit: int = 100) -> list[sqlite3.Row]:
|
||||
return list(_get_conn().execute(
|
||||
"""SELECT id, link FROM flats
|
||||
WHERE enrichment_status IN ('pending', 'failed')
|
||||
ORDER BY discovered_at DESC LIMIT ?""",
|
||||
(limit,),
|
||||
).fetchall())
|
||||
|
||||
|
||||
def enrichment_counts() -> dict:
|
||||
row = _get_conn().execute(
|
||||
"""SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(CASE WHEN enrichment_status = 'ok' THEN 1 ELSE 0 END) AS ok,
|
||||
SUM(CASE WHEN enrichment_status = 'pending' THEN 1 ELSE 0 END) AS pending,
|
||||
SUM(CASE WHEN enrichment_status = 'failed' THEN 1 ELSE 0 END) AS failed
|
||||
FROM flats"""
|
||||
).fetchone()
|
||||
return {
|
||||
"total": int(row["total"] or 0),
|
||||
"ok": int(row["ok"] or 0),
|
||||
"pending": int(row["pending"] or 0),
|
||||
"failed": int(row["failed"] or 0),
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Applications
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -232,19 +232,3 @@ def _spawn(coro) -> asyncio.Task:
|
|||
|
||||
def kick(flat_id: str) -> None:
|
||||
_spawn(asyncio.to_thread(enrich_flat_sync, flat_id))
|
||||
|
||||
|
||||
async def _backfill_runner() -> None:
|
||||
rows = db.flats_needing_enrichment(limit=200)
|
||||
logger.info("enrich backfill: %d flats queued", len(rows))
|
||||
for row in rows:
|
||||
try:
|
||||
await asyncio.to_thread(enrich_flat_sync, row["id"])
|
||||
except Exception:
|
||||
logger.exception("backfill step failed flat=%s", row["id"])
|
||||
|
||||
|
||||
def kick_backfill() -> int:
|
||||
pending = db.flats_needing_enrichment(limit=200)
|
||||
_spawn(_backfill_runner())
|
||||
return len(pending)
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ def on_match(user_id: int, flat: dict) -> None:
|
|||
|
||||
rent_str = _fmt_num(rent)
|
||||
if sqm_price:
|
||||
rent_str += f" ({sqm_price} €/m²)"
|
||||
rent_str += f" ({sqm_price:.0f} €/m²)"
|
||||
|
||||
body = (
|
||||
f"{line1}\n{line2}\n"
|
||||
|
|
|
|||
|
|
@ -79,7 +79,6 @@ def _wohnungen_context(user) -> dict:
|
|||
flats_view.append({"row": f, "last": latest_apps.get(f["id"])})
|
||||
|
||||
rejected_view = db.rejected_flats(uid)
|
||||
enrichment_counts = db.enrichment_counts()
|
||||
|
||||
partner = db.get_partner_user(uid)
|
||||
partner_info = None
|
||||
|
|
@ -133,7 +132,6 @@ def _wohnungen_context(user) -> dict:
|
|||
"flats": flats_view,
|
||||
"rejected_flats": rejected_view,
|
||||
"filtered_out_flats": filtered_out_view,
|
||||
"enrichment_counts": enrichment_counts,
|
||||
"partner": partner_info,
|
||||
"map_points": map_points,
|
||||
"has_filters": _has_filters(filters_row),
|
||||
|
|
@ -360,19 +358,6 @@ async def action_submit_forms(
|
|||
return RedirectResponse(request.headers.get("referer", "/einstellungen/profil"), status_code=303)
|
||||
|
||||
|
||||
@router.post("/actions/enrich-all")
|
||||
async def action_enrich_all(
|
||||
request: Request,
|
||||
csrf: str = Form(...),
|
||||
admin=Depends(require_admin),
|
||||
):
|
||||
require_csrf(admin["id"], csrf)
|
||||
queued = enrichment.kick_backfill()
|
||||
db.log_audit(admin["username"], "enrichment.backfill",
|
||||
f"queued={queued}", user_id=admin["id"], ip=client_ip(request))
|
||||
return _wohnungen_partial_or_redirect(request, admin)
|
||||
|
||||
|
||||
@router.post("/actions/enrich-flat")
|
||||
async def action_enrich_flat(
|
||||
request: Request,
|
||||
|
|
|
|||
|
|
@ -138,9 +138,8 @@ body:has(#v_map:checked) .view-map { display: block; }
|
|||
image. Prev arrow is hidden on the first image; next arrow on the last. */
|
||||
.lightbox { position: fixed; inset: 0; z-index: 1000;
|
||||
background: rgba(8, 18, 32, .92);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
align-items: center; justify-content: center;
|
||||
animation: lightbox-fade .15s ease-out; }
|
||||
.lightbox.hidden { display: none; }
|
||||
.lightbox-image { max-width: 92vw; max-height: 88vh; object-fit: contain;
|
||||
border-radius: 8px; box-shadow: 0 10px 40px rgba(0,0,0,.5);
|
||||
background: #0c1726; }
|
||||
|
|
|
|||
|
|
@ -131,9 +131,16 @@ document.addEventListener("DOMContentLoaded", openDeepLinkedFlat);
|
|||
|
||||
// Image lightbox — single global modal in base.html, opened by clicking any
|
||||
// .flat-gallery-tile. Click handler is delegated so it survives HTMX swaps.
|
||||
// Visibility is driven by inline style.display rather than a `hidden` class
|
||||
// because Tailwind's CDN injects its own `.hidden { display: none }` rule
|
||||
// at runtime, which conflicted with our class toggle and kept the modal
|
||||
// invisible after open().
|
||||
(function () {
|
||||
const overlay = document.getElementById("lazyflat-lightbox");
|
||||
if (!overlay) return;
|
||||
if (!overlay) {
|
||||
console.warn("[lazyflat.lightbox] #lazyflat-lightbox not in DOM; viewer disabled");
|
||||
return;
|
||||
}
|
||||
const imgEl = overlay.querySelector(".lightbox-image");
|
||||
const counterEl = overlay.querySelector(".lightbox-counter");
|
||||
const prevBtn = overlay.querySelector("[data-lightbox-prev]");
|
||||
|
|
@ -154,14 +161,14 @@ document.addEventListener("DOMContentLoaded", openDeepLinkedFlat);
|
|||
if (!list.length) return;
|
||||
urls = list;
|
||||
idx = Math.max(0, Math.min(startIdx | 0, urls.length - 1));
|
||||
overlay.classList.remove("hidden");
|
||||
overlay.style.display = "flex";
|
||||
overlay.setAttribute("aria-hidden", "false");
|
||||
document.body.classList.add("lightbox-open");
|
||||
render();
|
||||
}
|
||||
|
||||
function close() {
|
||||
overlay.classList.add("hidden");
|
||||
overlay.style.display = "none";
|
||||
overlay.setAttribute("aria-hidden", "true");
|
||||
document.body.classList.remove("lightbox-open");
|
||||
imgEl.removeAttribute("src");
|
||||
|
|
@ -182,7 +189,7 @@ document.addEventListener("DOMContentLoaded", openDeepLinkedFlat);
|
|||
if (ev.target === overlay) close();
|
||||
});
|
||||
document.addEventListener("keydown", (ev) => {
|
||||
if (overlay.classList.contains("hidden")) return;
|
||||
if (overlay.style.display === "none") return;
|
||||
if (ev.key === "Escape") close();
|
||||
else if (ev.key === "ArrowLeft") step(-1);
|
||||
else if (ev.key === "ArrowRight") step(1);
|
||||
|
|
|
|||
|
|
@ -84,17 +84,6 @@
|
|||
<span class="sep">·</span>
|
||||
<span>aktualisiert <span class="countdown" data-counter-up-utc="{{ last_scrape_utc }}">…</span></span>
|
||||
{% endif %}
|
||||
{% if is_admin and (enrichment_counts.pending or enrichment_counts.failed) %}
|
||||
<span class="sep">·</span>
|
||||
<form method="post" action="/actions/enrich-all"
|
||||
hx-post="/actions/enrich-all" hx-target="#wohnungen-body" hx-swap="outerHTML">
|
||||
<input type="hidden" name="csrf" value="{{ csrf }}">
|
||||
<button class="btn btn-ghost text-xs" type="submit"
|
||||
hx-confirm="Bilder für ausstehende Wohnungen nachladen? Kann einige Minuten dauern.">
|
||||
Bilder nachladen ({{ enrichment_counts.pending + enrichment_counts.failed }})
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<div class="view-toggle ml-2">
|
||||
<label>
|
||||
<input type="radio" name="view_mode" id="v_list" value="list" checked>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
{% block body %}{% endblock %}
|
||||
|
||||
{# Image lightbox — global so any flat-gallery on any page reuses the same modal. #}
|
||||
<div id="lazyflat-lightbox" class="lightbox hidden" aria-hidden="true" role="dialog" aria-label="Bildansicht">
|
||||
<div id="lazyflat-lightbox" class="lightbox" style="display:none" aria-hidden="true" role="dialog" aria-label="Bildansicht">
|
||||
<button class="lightbox-close" type="button" aria-label="Schließen" data-lightbox-close>
|
||||
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="6" x2="18" y2="18"/><line x1="18" y1="6" x2="6" y2="18"/></svg>
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue