From 714194615f9e02428ad3366d898bd34144984b1a Mon Sep 17 00:00:00 2001 From: kai Date: Tue, 25 Aug 2026 13:15:08 +0200 Subject: [PATCH] inaktive veranstaltungen zeigen keinen teilnahmeberreich mehr, veranstaltungsortsuche verbessert --- app/main.py | 29 +++++++++++++++++++--- app/templates/concert_detail.html | 23 ++++++++++++++++-- app/templates/new_concert.html | 40 +++++++++++++++++++++++++++++-- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/app/main.py b/app/main.py index 0c1bb0c..1b0f2f2 100644 --- a/app/main.py +++ b/app/main.py @@ -2,6 +2,7 @@ import os import uuid import secrets import hashlib +import re from contextlib import asynccontextmanager from datetime import datetime, timedelta @@ -1147,6 +1148,7 @@ def concert_detail(request: Request, concert_id: int): ] attending_users = [item for item in attendance if item["status"] == "attending"] ticket_seekers = [item for item in attendance if item["status"] == "ticket_search"] + maybe_users = [item for item in attendance if item["status"] == "maybe"] current_attendance = next( (item["status"] for item in attendance if item["user_id"] == user["id"]), None, @@ -1165,6 +1167,8 @@ def concert_detail(request: Request, concert_id: int): attending_count=len(attending_users), ticket_seekers=ticket_seekers, ticket_seeker_count=len(ticket_seekers), + maybe_users=maybe_users, + maybe_count=len(maybe_users), ) @@ -1606,6 +1610,12 @@ def search_venues(q: str): return [] results = [] + query_tokens = [ + token + for token in re.findall(r"[a-z0-9]+", q.lower()) + if len(token) >= 3 + ] + token_match = " AND ".join("name ILIKE %s" for _ in query_tokens) or "FALSE" # ======================================================== @@ -1616,7 +1626,7 @@ def search_venues(q: str): with connection.cursor() as cursor: - cursor.execute(""" + cursor.execute(f""" SELECT id, name, @@ -1633,6 +1643,7 @@ def search_venues(q: str): name ILIKE %s OR city ILIKE %s OR street ILIKE %s + OR ({token_match}) ORDER BY CASE WHEN LOWER(name) = LOWER(%s) @@ -1653,11 +1664,13 @@ def search_venues(q: str): f"%{q}%", + *[f"%{token}%" for token in query_tokens], + q, f"{q}%", - f"%{q}%" + f"%{q}%", )) @@ -1717,9 +1730,11 @@ def search_venues(q: str): "User-Agent": "PinguConcerts/1.0" } + external_query = re.sub(r"\bhall\b", "halle", q, flags=re.IGNORECASE) + params = { - "q": q, + "q": external_query, "format": "jsonv2", @@ -1876,6 +1891,14 @@ def search_venues(q: str): score = 0 + name_tokens = re.findall(r"[a-z0-9]+", name_lower) + matched_tokens = sum( + any(candidate.startswith(token) or token.startswith(candidate) + for candidate in name_tokens) + for token in query_tokens + ) + score += matched_tokens * 30 + if name_lower == query_lower: diff --git a/app/templates/concert_detail.html b/app/templates/concert_detail.html index 610805d..e04e04b 100644 --- a/app/templates/concert_detail.html +++ b/app/templates/concert_detail.html @@ -196,7 +196,9 @@ {% endif %} -
+ {% if not concert.is_past %} + +

🐧 Wer ist dabei? @@ -241,6 +243,7 @@
{{ attending_count }} Zusage{% if attending_count != 1 %}n{% endif %} + · {{ maybe_count }} Vielleicht · {{ ticket_seeker_count }} sucht Ticket @@ -260,6 +263,20 @@ {% endif %} +
+

? Vielleicht

+ + {% if maybe_users %} +
    + {% for maybe_user in maybe_users %} +
  • {{ maybe_user.name }}
  • + {% endfor %} +
+ {% else %} +

Noch keine Vielleicht-Angaben.

+ {% endif %} +
+

🎟️ Sucht ein Ticket

@@ -277,7 +294,9 @@
-

+
+ + {% endif %}
diff --git a/app/templates/new_concert.html b/app/templates/new_concert.html index 227dd61..a15ccbd 100644 --- a/app/templates/new_concert.html +++ b/app/templates/new_concert.html @@ -389,6 +389,27 @@ const selectedVenue = let searchTimeout = null; +let venueRequestController = null; + + +function clearVenueSelection() { + + [ + "venue-id", + "venue-name", + "venue-city", + "venue-street", + "venue-postal-code", + "venue-country", + "venue-latitude", + "venue-longitude" + ].forEach( + id => { + document.getElementById(id).value = ""; + } + ); + +} venueSearch.addEventListener( @@ -410,6 +431,8 @@ venueSearch.addEventListener( selectedVenue.style.display = "none"; + clearVenueSelection(); + if (query.length < 2) { @@ -426,7 +449,7 @@ venueSearch.addEventListener( ); }, - 300 + 450 ); } @@ -443,9 +466,18 @@ async function searchVenues( try { + if (venueRequestController) { + venueRequestController.abort(); + } + + venueRequestController = new AbortController(); + const response = await fetch( - `/api/venues/search?q=${encodeURIComponent(query)}` + `/api/venues/search?q=${encodeURIComponent(query)}`, + { + signal: venueRequestController.signal + } ); @@ -554,6 +586,10 @@ async function searchVenues( } catch (error) { + if (error.name === "AbortError") { + return; + } + console.error( "Venue search failed:", error