Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import os | |
| import threading | |
| from datetime import datetime, timedelta | |
| from huggingface_hub import InferenceClient | |
| ROWS = [chr(i) for i in range(ord("A"), ord("Z") + 1)] | |
| SEATS_PER_ROW = 26 | |
| SECTION_RANGES = { | |
| "left": range(1, 9), | |
| "middle": range(9, 19), | |
| "right": range(19, 27), | |
| } | |
| lock = threading.Lock() | |
| seat_state = {row: {seat: "available" for seat in range(1, 27)} for row in ROWS} | |
| holds = {} | |
| bookings = {} | |
| lock_owner = None | |
| lock_expires_at = None | |
| client = InferenceClient( | |
| base_url="https://router.huggingface.co/v1", | |
| token=os.getenv("HF_TOKEN") | |
| ) | |
| def seat_label(row, seat): | |
| return f"{row}{seat}" | |
| def section_for_seat(seat): | |
| if 1 <= seat <= 8: | |
| return "left" | |
| if 9 <= seat <= 18: | |
| return "middle" | |
| return "right" | |
| def render_map(): | |
| html = """ | |
| <style> | |
| .seat-grid { font-family: Arial, sans-serif; display: flex; flex-direction: column; gap: 6px; } | |
| .row { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; } | |
| .row-label { width: 24px; font-weight: bold; } | |
| .seat { | |
| display: inline-block; | |
| min-width: 34px; | |
| padding: 6px 8px; | |
| margin: 1px; | |
| border-radius: 6px; | |
| text-align: center; | |
| color: white; | |
| font-size: 12px; | |
| } | |
| .available { background: #2ecc71; } | |
| .booked { background: #e74c3c; } | |
| .held { background: #f39c12; } | |
| .gap { | |
| display: inline-block; | |
| width: 18px; | |
| } | |
| </style> | |
| <div class="seat-grid"> | |
| """ | |
| for row in ROWS: | |
| html += f'<div class="row"><span class="row-label">{row}</span>' | |
| for seat in range(1, 27): | |
| status = seat_state[row][seat] | |
| cls = "available" if status == "available" else "booked" if status == "booked" else "held" | |
| html += f'<span class="seat {cls}">{seat}</span>' | |
| if seat in (8, 18): | |
| html += '<span class="gap"></span>' | |
| html += "</div>" | |
| html += "</div>" | |
| return html | |
| def available_seats(): | |
| out = [] | |
| for row in ROWS: | |
| for seat in range(1, 27): | |
| if seat_state[row][seat] == "available": | |
| out.append(seat_label(row, seat)) | |
| return out | |
| def find_best_block(n): | |
| for row in ROWS: | |
| for section_name, seats in SECTION_RANGES.items(): | |
| seats = list(seats) | |
| run = [] | |
| for s in seats: | |
| if seat_state[row][s] == "available": | |
| run.append(s) | |
| if len(run) >= n: | |
| return [seat_label(row, x) for x in run[:n]] | |
| else: | |
| run = [] | |
| return None | |
| def acquire_lock(user_id): | |
| global lock_owner, lock_expires_at | |
| now = datetime.utcnow() | |
| if lock_owner and lock_expires_at and now < lock_expires_at and lock_owner != user_id: | |
| return False, f"Locked by {lock_owner}" | |
| lock_owner = user_id | |
| lock_expires_at = now + timedelta(minutes=10) | |
| return True, f"Lock granted to {user_id}" | |
| def release_lock(user_id): | |
| global lock_owner, lock_expires_at | |
| with lock: | |
| if lock_owner != user_id: | |
| return f"You do not own the lock." | |
| lock_owner = None | |
| lock_expires_at = None | |
| return "Lock released." | |
| def hold_seats(user_id, count): | |
| with lock: | |
| ok, msg = acquire_lock(user_id) | |
| if not ok: | |
| return render_map(), msg, "", "" | |
| seats = find_best_block(count) | |
| if not seats: | |
| return render_map(), "Not enough adjacent seats available.", "", "" | |
| hold_id = f"H{len(holds)+1}" | |
| for seat in seats: | |
| row = seat[0] | |
| num = int(seat[1:]) | |
| seat_state[row][num] = "held" | |
| holds[hold_id] = { | |
| "user_id": user_id, | |
| "seats": seats, | |
| "expires_at": datetime.utcnow() + timedelta(minutes=5), | |
| } | |
| return render_map(), f"Hold created: {hold_id}", hold_id, ", ".join(seats) | |
| def confirm_hold(user_id, hold_id): | |
| global lock_owner, lock_expires_at | |
| with lock: | |
| h = holds.get(hold_id) | |
| if not h: | |
| return render_map(), "Hold not found.", "" | |
| if h["user_id"] != user_id: | |
| return render_map(), "You do not own this hold.", "" | |
| for seat in h["seats"]: | |
| row = seat[0] | |
| num = int(seat[1:]) | |
| seat_state[row][num] = "booked" | |
| booking_id = f"B{len(bookings)+1}" | |
| bookings[booking_id] = h | |
| del holds[hold_id] | |
| lock_owner = None | |
| lock_expires_at = None | |
| return render_map(), f"Confirmed. Booking ID: {booking_id}. Seats: {', '.join(h['seats'])}", ", ".join(h["seats"]) | |
| def ai_chat(message, history): | |
| messages = [{"role": "system", "content": "You are a ticketing assistant for a venue seat booking app."}] | |
| for user_msg, bot_msg in history: | |
| messages.append({"role": "user", "content": user_msg}) | |
| messages.append({"role": "assistant", "content": bot_msg}) | |
| messages.append({"role": "user", "content": message}) | |
| if os.getenv("HF_TOKEN"): | |
| resp = client.chat.completions.create( | |
| model="meta-llama/Llama-3.1-8B-Instruct", | |
| messages=messages, | |
| max_tokens=200, | |
| temperature=0.2, | |
| ) | |
| return resp.choices[0].message.content | |
| return "HF_TOKEN is not configured." | |
| def hold_action(user_id, group_size): | |
| return hold_seats(user_id, int(group_size)) | |
| def confirm_action(user_id, hold_id): | |
| return confirm_hold(user_id, hold_id) | |
| def release_action(user_id): | |
| msg = release_lock(user_id) | |
| return render_map(), msg | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# Venue Ticketing System") | |
| with gr.Row(): | |
| user_id = gr.Textbox(label="User ID", value="user1") | |
| group_size = gr.Number(label="Seats needed", value=2, precision=0) | |
| with gr.Row(): | |
| btn_hold = gr.Button("Hold Seats") | |
| btn_confirm = gr.Button("Confirm Booking") | |
| btn_release = gr.Button("Release Lock") | |
| with gr.Row(): | |
| hold_id = gr.Textbox(label="Hold ID") | |
| seats_text = gr.Textbox(label="Seats", interactive=False) | |
| status = gr.Textbox(label="Status", interactive=False) | |
| seat_map = gr.HTML(label="Seat Map") | |
| ai = gr.ChatInterface(fn=ai_chat, title="AI Assistant") | |
| btn_hold.click( | |
| hold_action, | |
| inputs=[user_id, group_size], | |
| outputs=[seat_map, status, hold_id, seats_text], | |
| ) | |
| btn_confirm.click( | |
| confirm_action, | |
| inputs=[user_id, hold_id], | |
| outputs=[seat_map, status, seats_text], | |
| ) | |
| btn_release.click( | |
| release_action, | |
| inputs=[user_id], | |
| outputs=[seat_map, status], | |
| ) | |
| demo.load(render_map, outputs=seat_map) | |
| demo.queue(default_concurrency_limit=1) | |
| demo.launch() |