Add diary photos and linked patch history
This commit is contained in:
+157
-36
@@ -25,7 +25,7 @@ except ImportError:
|
||||
Image = ImageOps = UnidentifiedImageError = None
|
||||
|
||||
from fastapi import FastAPI, File, Form, Request, UploadFile
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, PlainTextResponse, RedirectResponse
|
||||
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, PlainTextResponse, RedirectResponse
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
@@ -64,10 +64,16 @@ PATCH_DIR = os.path.join(
|
||||
"patches"
|
||||
)
|
||||
|
||||
DIARY_PHOTO_DIR = os.path.join(
|
||||
os.environ.get("PRIVATE_UPLOAD_DIR", os.path.join(BASE_DIR, "private_uploads")),
|
||||
"diary"
|
||||
)
|
||||
|
||||
os.makedirs(UPLOAD_DIR, exist_ok=True)
|
||||
os.makedirs(PHOTO_DIR, exist_ok=True)
|
||||
os.makedirs(AVATAR_DIR, exist_ok=True)
|
||||
os.makedirs(PATCH_DIR, exist_ok=True)
|
||||
os.makedirs(DIARY_PHOTO_DIR, exist_ok=True)
|
||||
|
||||
SESSION_COOKIE = "pingu_session"
|
||||
SESSION_DAYS = 30
|
||||
@@ -224,11 +230,15 @@ def ensure_schema():
|
||||
CREATE TABLE IF NOT EXISTS user_badges (
|
||||
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||
badge_code VARCHAR(50) NOT NULL,
|
||||
trigger_concert_id INTEGER REFERENCES concerts(id) ON DELETE SET NULL,
|
||||
awarded_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (user_id, badge_code)
|
||||
)
|
||||
""",
|
||||
"""
|
||||
ALTER TABLE user_badges ADD COLUMN IF NOT EXISTS trigger_concert_id INTEGER REFERENCES concerts(id) ON DELETE SET NULL
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS badge_assets (
|
||||
badge_code VARCHAR(50) PRIMARY KEY,
|
||||
path TEXT NOT NULL,
|
||||
@@ -419,12 +429,16 @@ def ensure_schema():
|
||||
rating SMALLINT NOT NULL CHECK (rating BETWEEN 1 AND 5),
|
||||
favorite_song VARCHAR(255),
|
||||
notes TEXT,
|
||||
photo_path TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (user_id, concert_id)
|
||||
)
|
||||
""",
|
||||
"""
|
||||
ALTER TABLE concert_diary ADD COLUMN IF NOT EXISTS photo_path TEXT
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS concert_bands (
|
||||
concert_id INTEGER NOT NULL REFERENCES concerts(id) ON DELETE CASCADE,
|
||||
band_key VARCHAR(255) NOT NULL,
|
||||
@@ -1313,14 +1327,15 @@ def attended_concert_stats(user_id: int) -> dict:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT concerts.venue_id, concerts.event_type,
|
||||
concerts.start_datetime::date, COALESCE(venues.country, '')
|
||||
SELECT concerts.id, concerts.venue_id, concerts.event_type,
|
||||
concerts.start_datetime, COALESCE(venues.country, '')
|
||||
FROM concert_attendance
|
||||
JOIN concerts ON concerts.id = concert_attendance.concert_id
|
||||
LEFT JOIN venues ON venues.id = concerts.venue_id
|
||||
WHERE concert_attendance.user_id = %s
|
||||
AND concert_attendance.status = 'attending'
|
||||
AND COALESCE(concerts.end_datetime, concerts.start_datetime) < CURRENT_TIMESTAMP
|
||||
ORDER BY concerts.start_datetime, concerts.id
|
||||
""",
|
||||
(user_id,),
|
||||
)
|
||||
@@ -1330,16 +1345,42 @@ def attended_concert_stats(user_id: int) -> dict:
|
||||
countries = set()
|
||||
dates = []
|
||||
weekend_counts = {}
|
||||
for venue_id, event_type, concert_date, country_value in rows:
|
||||
badge_triggers = {}
|
||||
seen_countries = set()
|
||||
venue_event_ids = {}
|
||||
weekend_event_ids = {}
|
||||
previous_date = None
|
||||
for concert_id, venue_id, event_type, concert_datetime, country_value in rows:
|
||||
concert_date = concert_datetime.date()
|
||||
if venue_id is not None and event_type != "other":
|
||||
venue_counts[venue_id] = venue_counts.get(venue_id, 0) + 1
|
||||
venue_event_ids.setdefault(venue_id, []).append(concert_id)
|
||||
if venue_counts[venue_id] == 5:
|
||||
badge_triggers.setdefault("regular", concert_id)
|
||||
country = normalize_country(country_value)
|
||||
if country:
|
||||
countries.add(country)
|
||||
if country not in seen_countries:
|
||||
seen_countries.add(country)
|
||||
if country != "deutschland":
|
||||
badge_triggers.setdefault("border_breaker", concert_id)
|
||||
if len(seen_countries) == 5:
|
||||
badge_triggers.setdefault("globe_banger", concert_id)
|
||||
dates.append(concert_date)
|
||||
if previous_date and concert_date - previous_date == timedelta(days=1):
|
||||
badge_triggers.setdefault("double_trouble", concert_id)
|
||||
previous_date = concert_date
|
||||
if concert_date.isoweekday() >= 5:
|
||||
weekend_key = concert_date.isocalendar()[:2]
|
||||
weekend_counts[weekend_key] = weekend_counts.get(weekend_key, 0) + 1
|
||||
weekend_event_ids.setdefault(weekend_key, []).append(concert_id)
|
||||
if weekend_counts[weekend_key] == 3:
|
||||
badge_triggers.setdefault("iron_weekend", concert_id)
|
||||
|
||||
attendance_number = len(dates)
|
||||
for code, _name, _icon, threshold, _description, category in BADGE_DEFINITIONS:
|
||||
if category == "attendance" and threshold == attendance_number:
|
||||
badge_triggers[code] = concert_id
|
||||
|
||||
distinct_dates = sorted(set(dates))
|
||||
return {
|
||||
@@ -1352,6 +1393,7 @@ def attended_concert_stats(user_id: int) -> dict:
|
||||
for earlier, later in zip(distinct_dates, distinct_dates[1:])
|
||||
),
|
||||
"has_iron_weekend": max(weekend_counts.values(), default=0) >= 3,
|
||||
"badge_triggers": badge_triggers,
|
||||
}
|
||||
|
||||
|
||||
@@ -1376,25 +1418,25 @@ def grant_earned_badges(user_id: int, stats: dict, registered_at):
|
||||
(user_id,),
|
||||
)
|
||||
|
||||
# Konzert-Patches sind eine Level-Leiste: immer nur die höchste
|
||||
# erreichte Stufe anzeigen (ältere Stufen werden ersetzt).
|
||||
cursor.execute(
|
||||
"DELETE FROM user_badges WHERE user_id = %s AND badge_code = ANY(%s)",
|
||||
(user_id, list(ATTENDANCE_BADGE_CODES + VENUE_BADGE_CODES)),
|
||||
)
|
||||
for badge_code, _name, _icon, threshold, _description, category in BADGE_DEFINITIONS:
|
||||
if category == "attendance" and threshold is not None and stats["total"] >= threshold:
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO user_badges (user_id, badge_code, trigger_concert_id)
|
||||
VALUES (%s, %s, %s) ON CONFLICT (user_id, badge_code) DO UPDATE SET
|
||||
trigger_concert_id = COALESCE(user_badges.trigger_concert_id, EXCLUDED.trigger_concert_id)
|
||||
""",
|
||||
(user_id, badge_code, stats["badge_triggers"].get(badge_code)),
|
||||
)
|
||||
if highest_attendance_badge:
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO user_badges (user_id, badge_code)
|
||||
VALUES (%s, %s)
|
||||
ON CONFLICT (user_id, badge_code) DO NOTHING
|
||||
""",
|
||||
(user_id, highest_attendance_badge),
|
||||
"UPDATE user_badges SET trigger_concert_id = COALESCE(trigger_concert_id, %s) WHERE user_id = %s AND badge_code = %s",
|
||||
(stats["badge_triggers"].get(highest_attendance_badge), user_id, highest_attendance_badge),
|
||||
)
|
||||
if venue_badge:
|
||||
cursor.execute(
|
||||
"INSERT INTO user_badges (user_id, badge_code) VALUES (%s, %s) ON CONFLICT DO NOTHING",
|
||||
(user_id, venue_badge),
|
||||
"INSERT INTO user_badges (user_id, badge_code, trigger_concert_id) VALUES (%s, %s, %s) ON CONFLICT (user_id, badge_code) DO UPDATE SET trigger_concert_id = COALESCE(user_badges.trigger_concert_id, EXCLUDED.trigger_concert_id)",
|
||||
(user_id, venue_badge, stats["badge_triggers"].get(venue_badge)),
|
||||
)
|
||||
achievement_codes = []
|
||||
if stats["has_foreign_concert"]:
|
||||
@@ -1407,8 +1449,8 @@ def grant_earned_badges(user_id: int, stats: dict, registered_at):
|
||||
achievement_codes.append("iron_weekend")
|
||||
for badge_code in achievement_codes:
|
||||
cursor.execute(
|
||||
"INSERT INTO user_badges (user_id, badge_code) VALUES (%s, %s) ON CONFLICT DO NOTHING",
|
||||
(user_id, badge_code),
|
||||
"INSERT INTO user_badges (user_id, badge_code, trigger_concert_id) VALUES (%s, %s, %s) ON CONFLICT (user_id, badge_code) DO UPDATE SET trigger_concert_id = COALESCE(user_badges.trigger_concert_id, EXCLUDED.trigger_concert_id)",
|
||||
(user_id, badge_code, stats["badge_triggers"].get(badge_code)),
|
||||
)
|
||||
connection.commit()
|
||||
|
||||
@@ -1438,12 +1480,13 @@ def load_profile(username: str):
|
||||
return None
|
||||
|
||||
cursor.execute(
|
||||
"SELECT badge_code, awarded_at FROM user_badges WHERE user_id = %s",
|
||||
"SELECT badge_code, awarded_at, trigger_concert_id FROM user_badges WHERE user_id = %s",
|
||||
(row[0],),
|
||||
)
|
||||
badge_rows = cursor.fetchall()
|
||||
earned_codes = {badge_row[0] for badge_row in badge_rows}
|
||||
badge_awarded_at = {badge_row[0]: badge_row[1] for badge_row in badge_rows}
|
||||
badge_triggers = {badge_row[0]: badge_row[2] for badge_row in badge_rows}
|
||||
|
||||
is_founder = row[1].casefold() == "kai"
|
||||
if is_founder:
|
||||
@@ -1467,6 +1510,7 @@ def load_profile(username: str):
|
||||
"is_founder": is_founder,
|
||||
"earned_codes": earned_codes,
|
||||
"badge_awarded_at": badge_awarded_at,
|
||||
"badge_triggers": badge_triggers,
|
||||
}
|
||||
|
||||
|
||||
@@ -2566,6 +2610,11 @@ def render_profile(
|
||||
)
|
||||
profile = load_profile(username)
|
||||
badge_assets = load_badge_assets()
|
||||
highest_visible_attendance = next(
|
||||
(code for code, _name, _icon, threshold, _description, category in reversed(BADGE_DEFINITIONS)
|
||||
if category == "attendance" and threshold is not None and attendance_stats["total"] >= threshold),
|
||||
None,
|
||||
)
|
||||
badges = [
|
||||
{
|
||||
"code": code,
|
||||
@@ -2574,12 +2623,23 @@ def render_profile(
|
||||
"threshold": threshold,
|
||||
"description": description,
|
||||
"category": category,
|
||||
"earned": code in profile["earned_codes"],
|
||||
"earned": code in profile["earned_codes"] and (category != "attendance" or code == highest_visible_attendance),
|
||||
"image_path": badge_assets.get(code),
|
||||
"awarded_at": (profile["badge_awarded_at"].get(code) or profile["registered_at"]).strftime("%d.%m.%Y"),
|
||||
"trigger_concert": None,
|
||||
"sort_key": (0, datetime.min) if code == "founder" else (1, datetime.min) if code == "admin" else (2, profile["badge_awarded_at"].get(code) or datetime.max),
|
||||
}
|
||||
for code, name, icon, threshold, description, category in BADGE_DEFINITIONS
|
||||
]
|
||||
for badge in badges:
|
||||
trigger_id = profile["badge_triggers"].get(badge["code"])
|
||||
if trigger_id:
|
||||
trigger_concert = load_concert(trigger_id)
|
||||
if trigger_concert and can_view_event(viewer, trigger_concert):
|
||||
badge["trigger_concert"] = {
|
||||
"id": trigger_id, "artist": trigger_concert["artist"],
|
||||
"date": trigger_concert["start_datetime"].strftime("%d.%m.%Y"),
|
||||
}
|
||||
badges.sort(key=lambda badge: badge["sort_key"])
|
||||
|
||||
template = templates.get_template("profile.html")
|
||||
@@ -2729,14 +2789,14 @@ def export_profile_data(request: Request):
|
||||
)
|
||||
followed_venues = cursor.fetchall()
|
||||
cursor.execute(
|
||||
"SELECT concert_id, rating, favorite_song, notes, created_at, updated_at FROM concert_diary WHERE user_id = %s ORDER BY updated_at",
|
||||
"SELECT concert_id, rating, favorite_song, notes, photo_path, created_at, updated_at FROM concert_diary WHERE user_id = %s ORDER BY updated_at",
|
||||
(user_id,),
|
||||
)
|
||||
diary_entries = cursor.fetchall()
|
||||
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT badge_code, awarded_at FROM user_badges
|
||||
SELECT badge_code, awarded_at, trigger_concert_id FROM user_badges
|
||||
WHERE user_id = %s ORDER BY awarded_at
|
||||
""",
|
||||
(user_id,),
|
||||
@@ -2769,8 +2829,8 @@ def export_profile_data(request: Request):
|
||||
"photos": rows_to_dicts(photos, ("id", "concert_id", "path", "created_at")),
|
||||
"followed_bands": rows_to_dicts(followed_bands, ("band_key", "display_name", "created_at")),
|
||||
"followed_venues": rows_to_dicts(followed_venues, ("venue_id", "created_at")),
|
||||
"concert_diary": rows_to_dicts(diary_entries, ("concert_id", "rating", "favorite_song", "notes", "created_at", "updated_at")),
|
||||
"badges": rows_to_dicts(badges, ("badge_code", "awarded_at")),
|
||||
"concert_diary": rows_to_dicts(diary_entries, ("concert_id", "rating", "favorite_song", "notes", "photo_path", "created_at", "updated_at")),
|
||||
"badges": rows_to_dicts(badges, ("badge_code", "awarded_at", "trigger_concert_id")),
|
||||
}
|
||||
filename = re.sub(r"[^A-Za-z0-9_-]", "_", user["username"])
|
||||
return JSONResponse(
|
||||
@@ -2794,11 +2854,13 @@ def delete_own_account(request: Request):
|
||||
photo_paths = [row[0] for row in cursor.fetchall()]
|
||||
cursor.execute("SELECT flyer_path FROM concerts WHERE created_by = %s AND flyer_path IS NOT NULL", (user_id,))
|
||||
flyer_paths = [row[0] for row in cursor.fetchall()]
|
||||
cursor.execute("SELECT photo_path FROM concert_diary WHERE user_id = %s AND photo_path IS NOT NULL", (user_id,))
|
||||
diary_photo_paths = [row[0] for row in cursor.fetchall()]
|
||||
cursor.execute("UPDATE registration_invites SET used_by = NULL WHERE used_by = %s", (user_id,))
|
||||
cursor.execute("UPDATE concerts SET flyer_path = NULL WHERE created_by = %s", (user_id,))
|
||||
cursor.execute("DELETE FROM users WHERE id = %s", (user_id,))
|
||||
connection.commit()
|
||||
for path, directory, prefix in [(avatar_path, AVATAR_DIR, "/static/uploads/avatars/")] + [(p, PHOTO_DIR, "/static/uploads/photos/") for p in photo_paths] + [(p, UPLOAD_DIR, "/static/uploads/flyers/") for p in flyer_paths]:
|
||||
for path, directory, prefix in [(avatar_path, AVATAR_DIR, "/static/uploads/avatars/")] + [(p, PHOTO_DIR, "/static/uploads/photos/") for p in photo_paths] + [(p, UPLOAD_DIR, "/static/uploads/flyers/") for p in flyer_paths] + [(p, DIARY_PHOTO_DIR, "/diary/photo/") for p in diary_photo_paths]:
|
||||
remove_uploaded_file(path, directory, prefix)
|
||||
response = RedirectResponse("/login", status_code=303)
|
||||
response.delete_cookie(SESSION_COOKIE, path="/", secure=COOKIE_SECURE, samesite="lax")
|
||||
@@ -3337,7 +3399,7 @@ def diary_page(request: Request):
|
||||
"""
|
||||
SELECT d.concert_id, c.artist, c.start_datetime, d.rating,
|
||||
COALESCE(d.favorite_song, ''), COALESCE(d.notes, ''),
|
||||
COALESCE(v.name, ''), COALESCE(v.city, '')
|
||||
COALESCE(v.name, ''), COALESCE(v.city, ''), d.photo_path
|
||||
FROM concert_diary d JOIN concerts c ON c.id = d.concert_id
|
||||
LEFT JOIN venues v ON v.id = c.venue_id
|
||||
WHERE d.user_id = %s ORDER BY c.start_datetime DESC
|
||||
@@ -3347,16 +3409,57 @@ def diary_page(request: Request):
|
||||
entries = [
|
||||
{"concert_id": row[0], "artist": row[1], "date": row[2].strftime("%d.%m.%Y"),
|
||||
"rating": row[3], "favorite_song": row[4], "notes": row[5],
|
||||
"venue": ", ".join(filter(None, (row[6], row[7])))}
|
||||
"venue": ", ".join(filter(None, (row[6], row[7]))), "photo_path": row[8]}
|
||||
for row in cursor.fetchall()
|
||||
]
|
||||
cursor.execute(
|
||||
"""
|
||||
SELECT ub.badge_code, ub.trigger_concert_id, ub.awarded_at, ba.path
|
||||
FROM user_badges ub LEFT JOIN badge_assets ba ON ba.badge_code = ub.badge_code
|
||||
WHERE ub.user_id = %s AND ub.trigger_concert_id IS NOT NULL
|
||||
ORDER BY ub.awarded_at
|
||||
""",
|
||||
(user["id"],),
|
||||
)
|
||||
diary_badges = {}
|
||||
for badge_code, concert_id, awarded_at, image_path in cursor.fetchall():
|
||||
definition = BADGE_BY_CODE.get(badge_code)
|
||||
if definition:
|
||||
name, icon, _threshold, description, _category = definition
|
||||
diary_badges.setdefault(concert_id, []).append({
|
||||
"name": name, "icon": icon, "description": description,
|
||||
"image_path": image_path, "awarded_at": awarded_at.strftime("%d.%m.%Y"),
|
||||
})
|
||||
for entry in entries:
|
||||
entry["badges"] = diary_badges.get(entry["concert_id"], [])
|
||||
return templates.get_template("diary.html").render(user=user, entries=entries)
|
||||
|
||||
|
||||
@app.get("/diary/photo/{filename}")
|
||||
def diary_photo(request: Request, filename: str):
|
||||
user = get_current_user(request)
|
||||
if not filename or os.path.basename(filename) != filename:
|
||||
return HTMLResponse("Bild nicht gefunden.", status_code=404)
|
||||
photo_path = f"/diary/photo/{filename}"
|
||||
with get_db_connection() as connection:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(
|
||||
"SELECT 1 FROM concert_diary WHERE user_id = %s AND photo_path = %s",
|
||||
(user["id"], photo_path),
|
||||
)
|
||||
if not cursor.fetchone():
|
||||
return HTMLResponse("Bild nicht gefunden.", status_code=404)
|
||||
file_path = os.path.join(DIARY_PHOTO_DIR, filename)
|
||||
if not os.path.isfile(file_path):
|
||||
return HTMLResponse("Bild nicht gefunden.", status_code=404)
|
||||
return FileResponse(file_path)
|
||||
|
||||
|
||||
@app.post("/concerts/{concert_id}/diary")
|
||||
def save_diary_entry(
|
||||
request: Request, concert_id: int, rating: int = Form(...),
|
||||
favorite_song: str = Form(""), notes: str = Form("")
|
||||
favorite_song: str = Form(""), notes: str = Form(""),
|
||||
photo: UploadFile | None = File(None), remove_photo: str = Form("")
|
||||
):
|
||||
user = get_current_user(request)
|
||||
concert = load_concert(concert_id)
|
||||
@@ -3376,17 +3479,30 @@ def save_diary_entry(
|
||||
)
|
||||
if not cursor.fetchone():
|
||||
return HTMLResponse("Ein Tagebucheintrag ist nur für besuchte Konzerte möglich.", status_code=403)
|
||||
cursor.execute(
|
||||
"SELECT photo_path FROM concert_diary WHERE user_id = %s AND concert_id = %s",
|
||||
(user["id"], concert_id),
|
||||
)
|
||||
previous_row = cursor.fetchone()
|
||||
previous_photo = previous_row[0] if previous_row else None
|
||||
new_photo, error = save_image(photo, DIARY_PHOTO_DIR, "/diary/photo/")
|
||||
if error:
|
||||
return error
|
||||
photo_path = new_photo or (None if remove_photo else previous_photo)
|
||||
cursor.execute(
|
||||
"""
|
||||
INSERT INTO concert_diary (user_id, concert_id, rating, favorite_song, notes)
|
||||
VALUES (%s, %s, %s, %s, %s)
|
||||
INSERT INTO concert_diary (user_id, concert_id, rating, favorite_song, notes, photo_path)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
ON CONFLICT (user_id, concert_id) DO UPDATE SET
|
||||
rating = EXCLUDED.rating, favorite_song = EXCLUDED.favorite_song,
|
||||
notes = EXCLUDED.notes, updated_at = CURRENT_TIMESTAMP
|
||||
notes = EXCLUDED.notes, photo_path = EXCLUDED.photo_path,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
""",
|
||||
(user["id"], concert_id, rating, favorite_song or None, notes or None),
|
||||
(user["id"], concert_id, rating, favorite_song or None, notes or None, photo_path),
|
||||
)
|
||||
connection.commit()
|
||||
if previous_photo and previous_photo != photo_path:
|
||||
remove_uploaded_file(previous_photo, DIARY_PHOTO_DIR, "/diary/photo/")
|
||||
return RedirectResponse(f"/concerts/{concert_id}#diary", status_code=303)
|
||||
|
||||
|
||||
@@ -3395,8 +3511,12 @@ def delete_diary_entry(request: Request, concert_id: int):
|
||||
user = get_current_user(request)
|
||||
with get_db_connection() as connection:
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute("SELECT photo_path FROM concert_diary WHERE user_id = %s AND concert_id = %s", (user["id"], concert_id))
|
||||
row = cursor.fetchone()
|
||||
cursor.execute("DELETE FROM concert_diary WHERE user_id = %s AND concert_id = %s", (user["id"], concert_id))
|
||||
connection.commit()
|
||||
if row and row[0]:
|
||||
remove_uploaded_file(row[0], DIARY_PHOTO_DIR, "/diary/photo/")
|
||||
return RedirectResponse(f"/concerts/{concert_id}#diary", status_code=303)
|
||||
|
||||
|
||||
@@ -3533,7 +3653,7 @@ def concert_detail(request: Request, concert_id: int):
|
||||
else:
|
||||
follows_venue = False
|
||||
cursor.execute(
|
||||
"SELECT rating, COALESCE(favorite_song, ''), COALESCE(notes, '') FROM concert_diary WHERE user_id = %s AND concert_id = %s",
|
||||
"SELECT rating, COALESCE(favorite_song, ''), COALESCE(notes, ''), photo_path FROM concert_diary WHERE user_id = %s AND concert_id = %s",
|
||||
(user["id"], concert_id),
|
||||
)
|
||||
diary_row = cursor.fetchone()
|
||||
@@ -3577,7 +3697,8 @@ def concert_detail(request: Request, concert_id: int):
|
||||
None,
|
||||
)
|
||||
diary_entry = {
|
||||
"rating": diary_row[0], "favorite_song": diary_row[1], "notes": diary_row[2]
|
||||
"rating": diary_row[0], "favorite_song": diary_row[1], "notes": diary_row[2],
|
||||
"photo_path": diary_row[3]
|
||||
} if diary_row else None
|
||||
concert_bands = load_concert_bands(concert_id, concert["artist"], concert["event_type"])
|
||||
for band in concert_bands:
|
||||
|
||||
Reference in New Issue
Block a user