Spaces:
Sleeping
Sleeping
Upload app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,447 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import os
|
| 3 |
+
from datetime import datetime
|
| 4 |
+
from supabase import create_client, Client
|
| 5 |
+
|
| 6 |
+
# ============ SUPABASE CONFIGURATION ============
|
| 7 |
+
SUPABASE_URL = os.getenv("SUPABASE_URL")
|
| 8 |
+
SUPABASE_KEY = os.getenv("SUPABASE_KEY")
|
| 9 |
+
|
| 10 |
+
if not SUPABASE_URL or not SUPABASE_KEY:
|
| 11 |
+
raise ValueError("β SUPABASE_URL or SUPABASE_KEY not set in Space Secrets!")
|
| 12 |
+
|
| 13 |
+
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
|
| 14 |
+
|
| 15 |
+
# ============ SUPABASE HELPER FUNCTIONS ============
|
| 16 |
+
def fetch_books():
|
| 17 |
+
"""Fetch all books from Supabase"""
|
| 18 |
+
try:
|
| 19 |
+
res = supabase.table("books").select("*").execute()
|
| 20 |
+
return res.data or []
|
| 21 |
+
except Exception as e:
|
| 22 |
+
print(f"Error fetching books: {e}")
|
| 23 |
+
return []
|
| 24 |
+
|
| 25 |
+
def fetch_borrows():
|
| 26 |
+
"""Fetch all active borrow records from Supabase"""
|
| 27 |
+
try:
|
| 28 |
+
res = supabase.table("borrows").select("*").execute()
|
| 29 |
+
return res.data or []
|
| 30 |
+
except Exception as e:
|
| 31 |
+
print(f"Error fetching borrows: {e}")
|
| 32 |
+
return []
|
| 33 |
+
|
| 34 |
+
def fetch_history():
|
| 35 |
+
"""Fetch all transactions from Supabase"""
|
| 36 |
+
try:
|
| 37 |
+
res = supabase.table("transactions").select("entry").execute()
|
| 38 |
+
entries = [row["entry"] for row in (res.data or [])]
|
| 39 |
+
return entries[-30:] # Return last 30 entries
|
| 40 |
+
except Exception as e:
|
| 41 |
+
print(f"Error fetching history: {e}")
|
| 42 |
+
return []
|
| 43 |
+
|
| 44 |
+
def book_titles():
|
| 45 |
+
"""Get list of book titles for dropdowns - ALWAYS FRESH FROM DB"""
|
| 46 |
+
books = fetch_books()
|
| 47 |
+
return [b["title"] for b in books]
|
| 48 |
+
|
| 49 |
+
# ============ DASHBOARD FUNCTIONS ============
|
| 50 |
+
def get_dashboard_stats():
|
| 51 |
+
books = fetch_books()
|
| 52 |
+
total_books = len(books)
|
| 53 |
+
total_copies = sum(b["total"] for b in books)
|
| 54 |
+
available_copies = sum(b["copies"] for b in books)
|
| 55 |
+
borrowed_copies = total_copies - available_copies
|
| 56 |
+
|
| 57 |
+
return f"""
|
| 58 |
+
π **Library Dashboard**
|
| 59 |
+
|
| 60 |
+
π Total Books: **{total_books}**
|
| 61 |
+
β
Available Copies: **{available_copies}**
|
| 62 |
+
π€ Borrowed Copies: **{borrowed_copies}**
|
| 63 |
+
π¦ Total Copies: **{total_copies}**
|
| 64 |
+
"""
|
| 65 |
+
|
| 66 |
+
def get_library_table():
|
| 67 |
+
rows = []
|
| 68 |
+
for b in fetch_books():
|
| 69 |
+
status = "β
Available" if b["copies"] > 0 else "β Out of Stock"
|
| 70 |
+
rows.append([b["title"], b["author"], b["copies"], b["total"], status])
|
| 71 |
+
return rows
|
| 72 |
+
|
| 73 |
+
# ============ BROWSE FUNCTIONS ============
|
| 74 |
+
def browse_books(search_query="", sort_by="Title"):
|
| 75 |
+
books_list = []
|
| 76 |
+
for b in fetch_books():
|
| 77 |
+
if search_query.lower() in b["title"].lower() or search_query.lower() in b["author"].lower():
|
| 78 |
+
books_list.append({
|
| 79 |
+
"Title": b["title"],
|
| 80 |
+
"Author": b["author"],
|
| 81 |
+
"Available": b["copies"],
|
| 82 |
+
"Total": b["total"],
|
| 83 |
+
"Status": "β
Available" if b["copies"] > 0 else "β Out of Stock"
|
| 84 |
+
})
|
| 85 |
+
|
| 86 |
+
if sort_by == "Title":
|
| 87 |
+
books_list.sort(key=lambda x: x["Title"])
|
| 88 |
+
elif sort_by == "Author":
|
| 89 |
+
books_list.sort(key=lambda x: x["Author"])
|
| 90 |
+
elif sort_by == "Availability":
|
| 91 |
+
books_list.sort(key=lambda x: x["Available"], reverse=True)
|
| 92 |
+
|
| 93 |
+
return books_list
|
| 94 |
+
|
| 95 |
+
# ============ BORROW FUNCTION ============
|
| 96 |
+
def borrow_book(selected_book, num_copies, name, phone, address):
|
| 97 |
+
if not selected_book or selected_book == "":
|
| 98 |
+
return "β Please select a book first"
|
| 99 |
+
|
| 100 |
+
if not name or not phone or not address:
|
| 101 |
+
return "β Please fill in all personal details (Name, Phone, Address)"
|
| 102 |
+
|
| 103 |
+
try:
|
| 104 |
+
num_copies = int(num_copies) if num_copies else 1
|
| 105 |
+
if num_copies < 1:
|
| 106 |
+
return "β Number of copies must be at least 1"
|
| 107 |
+
except Exception as e:
|
| 108 |
+
return f"β Invalid number of copies: {e}"
|
| 109 |
+
|
| 110 |
+
try:
|
| 111 |
+
# Get book from database
|
| 112 |
+
res = supabase.table("books").select("*").eq("title", selected_book).execute()
|
| 113 |
+
books = res.data
|
| 114 |
+
if not books:
|
| 115 |
+
return "β Book not found in library"
|
| 116 |
+
|
| 117 |
+
book = books[0]
|
| 118 |
+
available = book["copies"]
|
| 119 |
+
|
| 120 |
+
if available < num_copies:
|
| 121 |
+
return f"π Sorry, only {available} copies available. You requested {num_copies}"
|
| 122 |
+
|
| 123 |
+
# Update copies in database
|
| 124 |
+
supabase.table("books").update(
|
| 125 |
+
{"copies": available - num_copies}
|
| 126 |
+
).eq("id", book["id"]).execute()
|
| 127 |
+
|
| 128 |
+
# Record borrow
|
| 129 |
+
now = datetime.now().strftime('%Y-%m-%d %H:%M')
|
| 130 |
+
supabase.table("borrows").insert({
|
| 131 |
+
"book_title": selected_book,
|
| 132 |
+
"name": name,
|
| 133 |
+
"phone": phone,
|
| 134 |
+
"address": address,
|
| 135 |
+
"copies": num_copies,
|
| 136 |
+
"date": now
|
| 137 |
+
}).execute()
|
| 138 |
+
|
| 139 |
+
# Add to transaction history
|
| 140 |
+
entry = f"[{now}] BORROWED - Book: {selected_book} | Copies: {num_copies} | Borrower: {name} | Phone: {phone} | Address: {address}"
|
| 141 |
+
supabase.table("transactions").insert({"entry": entry}).execute()
|
| 142 |
+
|
| 143 |
+
return f"β
{name} successfully borrowed {num_copies} copy/copies of '{selected_book}'! Enjoy reading! π"
|
| 144 |
+
|
| 145 |
+
except Exception as e:
|
| 146 |
+
return f"β Error processing borrow: {e}"
|
| 147 |
+
|
| 148 |
+
# ============ RETURN FUNCTION ============
|
| 149 |
+
def return_book(selected_book, returner_name):
|
| 150 |
+
if not selected_book or selected_book == "":
|
| 151 |
+
return "β Please select a book first"
|
| 152 |
+
|
| 153 |
+
if not returner_name:
|
| 154 |
+
return "β Please enter your name"
|
| 155 |
+
|
| 156 |
+
try:
|
| 157 |
+
# Find borrow record
|
| 158 |
+
res = supabase.table("borrows").select("*") \
|
| 159 |
+
.eq("book_title", selected_book) \
|
| 160 |
+
.eq("name", returner_name).execute()
|
| 161 |
+
records = res.data or []
|
| 162 |
+
|
| 163 |
+
if not records:
|
| 164 |
+
return f"β No record found for {returner_name} borrowing '{selected_book}'"
|
| 165 |
+
|
| 166 |
+
record = records[0]
|
| 167 |
+
copies = record["copies"]
|
| 168 |
+
|
| 169 |
+
# Get book
|
| 170 |
+
res = supabase.table("books").select("*").eq("title", selected_book).execute()
|
| 171 |
+
books = res.data
|
| 172 |
+
if not books:
|
| 173 |
+
return "π This book doesn't belong to our library"
|
| 174 |
+
|
| 175 |
+
book = books[0]
|
| 176 |
+
|
| 177 |
+
# Update book copies
|
| 178 |
+
supabase.table("books").update(
|
| 179 |
+
{"copies": book["copies"] + copies}
|
| 180 |
+
).eq("id", book["id"]).execute()
|
| 181 |
+
|
| 182 |
+
# Delete borrow record
|
| 183 |
+
supabase.table("borrows").delete().eq("id", record["id"]).execute()
|
| 184 |
+
|
| 185 |
+
# Add to transaction history
|
| 186 |
+
now = datetime.now().strftime('%Y-%m-%d %H:%M')
|
| 187 |
+
entry = f"[{now}] RETURNED - Book: {selected_book} | Copies: {copies} | Returner: {returner_name} | Phone: {record['phone']} | Address: {record['address']}"
|
| 188 |
+
supabase.table("transactions").insert({"entry": entry}).execute()
|
| 189 |
+
|
| 190 |
+
return f"β
Thank you {returner_name} for returning {copies} copy/copies of '{selected_book}'! π"
|
| 191 |
+
|
| 192 |
+
except Exception as e:
|
| 193 |
+
return f"β Error processing return: {e}"
|
| 194 |
+
|
| 195 |
+
# ============ ADD BOOK FUNCTION ============
|
| 196 |
+
def add_book(title, author, copies):
|
| 197 |
+
if not title or not author or not copies:
|
| 198 |
+
return "β Please fill all fields correctly"
|
| 199 |
+
|
| 200 |
+
try:
|
| 201 |
+
copies = int(copies)
|
| 202 |
+
if copies < 1:
|
| 203 |
+
return "β Number of copies must be at least 1"
|
| 204 |
+
|
| 205 |
+
# Check if book exists
|
| 206 |
+
res = supabase.table("books").select("id").eq("title", title).execute()
|
| 207 |
+
if res.data:
|
| 208 |
+
return f"β Book '{title}' already exists in the library"
|
| 209 |
+
|
| 210 |
+
# Insert new book
|
| 211 |
+
supabase.table("books").insert({
|
| 212 |
+
"title": title,
|
| 213 |
+
"author": author,
|
| 214 |
+
"copies": copies,
|
| 215 |
+
"total": copies
|
| 216 |
+
}).execute()
|
| 217 |
+
|
| 218 |
+
# Add to transaction history
|
| 219 |
+
entry = f"[{datetime.now().strftime('%Y-%m-%d %H:%M')}] ADDED - Book: {title} by {author} ({copies} copies)"
|
| 220 |
+
supabase.table("transactions").insert({"entry": entry}).execute()
|
| 221 |
+
|
| 222 |
+
return f"β
'{title}' successfully added to library! π"
|
| 223 |
+
|
| 224 |
+
except Exception as e:
|
| 225 |
+
return f"β Error adding book: {e}"
|
| 226 |
+
|
| 227 |
+
# ============ HISTORY FUNCTION ============
|
| 228 |
+
def get_history():
|
| 229 |
+
rows = fetch_history()
|
| 230 |
+
if not rows:
|
| 231 |
+
return "No transactions yet."
|
| 232 |
+
return "\n".join(rows)
|
| 233 |
+
|
| 234 |
+
# ============ GET BORROWER DETAILS FUNCTION ============
|
| 235 |
+
def get_borrower_details():
|
| 236 |
+
records = fetch_borrows()
|
| 237 |
+
if not records:
|
| 238 |
+
return "No active borrowing records."
|
| 239 |
+
|
| 240 |
+
details = "π **Active Borrowing Records**\n\n"
|
| 241 |
+
by_book = {}
|
| 242 |
+
for r in records:
|
| 243 |
+
by_book.setdefault(r["book_title"], []).append(r)
|
| 244 |
+
|
| 245 |
+
for title, recs in by_book.items():
|
| 246 |
+
details += f"**π {title}**\n"
|
| 247 |
+
for r in recs:
|
| 248 |
+
details += f" β’ Name: {r['name']}\n"
|
| 249 |
+
details += f" β’ Phone: {r['phone']}\n"
|
| 250 |
+
details += f" β’ Address: {r['address']}\n"
|
| 251 |
+
details += f" β’ Copies: {r['copies']}\n"
|
| 252 |
+
details += f" β’ Borrowed on: {r['date']}\n\n"
|
| 253 |
+
|
| 254 |
+
return details
|
| 255 |
+
|
| 256 |
+
# ============ GRADIO INTERFACE ============
|
| 257 |
+
demo = gr.Blocks(title="π Library Management System")
|
| 258 |
+
|
| 259 |
+
with demo:
|
| 260 |
+
gr.Markdown("# π Library Management System")
|
| 261 |
+
gr.Markdown("*Your Smart Library Management Solution (Powered by Supabase)*")
|
| 262 |
+
|
| 263 |
+
# ===== TAB 1: DASHBOARD =====
|
| 264 |
+
with gr.Tab("π Dashboard"):
|
| 265 |
+
dashboard_output = gr.Markdown(get_dashboard_stats())
|
| 266 |
+
gr.Markdown("### π Book Collection")
|
| 267 |
+
|
| 268 |
+
def update_dashboard():
|
| 269 |
+
return get_dashboard_stats()
|
| 270 |
+
|
| 271 |
+
def refresh_dashboard_table():
|
| 272 |
+
return get_library_table()
|
| 273 |
+
|
| 274 |
+
demo.load(update_dashboard, outputs=dashboard_output)
|
| 275 |
+
|
| 276 |
+
library_table = gr.Dataframe(
|
| 277 |
+
value=get_library_table(),
|
| 278 |
+
headers=["Title", "Author", "Available", "Total", "Status"],
|
| 279 |
+
interactive=False,
|
| 280 |
+
label="π Books in Library"
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
refresh_dashboard_btn = gr.Button("π Refresh Dashboard")
|
| 284 |
+
refresh_dashboard_btn.click(update_dashboard, outputs=dashboard_output)
|
| 285 |
+
refresh_dashboard_btn.click(refresh_dashboard_table, outputs=library_table)
|
| 286 |
+
|
| 287 |
+
# ===== TAB 2: BROWSE BOOKS =====
|
| 288 |
+
with gr.Tab("π Browse Books"):
|
| 289 |
+
search_input = gr.Textbox(label="Search by title or author", placeholder="e.g., 'Atomic' or 'James Clear'")
|
| 290 |
+
sort_dropdown = gr.Dropdown(
|
| 291 |
+
choices=["Title", "Author", "Availability"],
|
| 292 |
+
value="Title",
|
| 293 |
+
label="Sort by"
|
| 294 |
+
)
|
| 295 |
+
|
| 296 |
+
browse_output = gr.Dataframe(
|
| 297 |
+
headers=["Title", "Author", "Available", "Total", "Status"],
|
| 298 |
+
interactive=False,
|
| 299 |
+
label="π Search Results"
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
def update_browse(search, sort):
|
| 303 |
+
results = browse_books(search, sort)
|
| 304 |
+
if results:
|
| 305 |
+
return [[r["Title"], r["Author"], r["Available"], r["Total"], r["Status"]] for r in results]
|
| 306 |
+
return []
|
| 307 |
+
|
| 308 |
+
search_input.change(update_browse, inputs=[search_input, sort_dropdown], outputs=browse_output)
|
| 309 |
+
sort_dropdown.change(update_browse, inputs=[search_input, sort_dropdown], outputs=browse_output)
|
| 310 |
+
|
| 311 |
+
# ===== TAB 3: BORROW BOOK =====
|
| 312 |
+
with gr.Tab("π€ Borrow Book"):
|
| 313 |
+
gr.Markdown("### Borrow a book from the library")
|
| 314 |
+
|
| 315 |
+
books_list = book_titles()
|
| 316 |
+
|
| 317 |
+
with gr.Row():
|
| 318 |
+
with gr.Column(scale=1):
|
| 319 |
+
borrow_select = gr.Dropdown(
|
| 320 |
+
choices=books_list if books_list else ["No books available"],
|
| 321 |
+
value=books_list[0] if books_list else None,
|
| 322 |
+
label="Select Book *",
|
| 323 |
+
interactive=True
|
| 324 |
+
)
|
| 325 |
+
|
| 326 |
+
borrow_copies = gr.Slider(
|
| 327 |
+
label="Number of Copies to Borrow *",
|
| 328 |
+
minimum=1,
|
| 329 |
+
maximum=10,
|
| 330 |
+
step=1,
|
| 331 |
+
value=1
|
| 332 |
+
)
|
| 333 |
+
|
| 334 |
+
with gr.Column(scale=1):
|
| 335 |
+
borrower_name = gr.Textbox(
|
| 336 |
+
label="Your Name *",
|
| 337 |
+
placeholder="e.g., 'John Doe'"
|
| 338 |
+
)
|
| 339 |
+
|
| 340 |
+
borrower_phone = gr.Textbox(
|
| 341 |
+
label="Your Phone Number *",
|
| 342 |
+
placeholder="e.g., '9876543210'"
|
| 343 |
+
)
|
| 344 |
+
|
| 345 |
+
borrower_address = gr.Textbox(
|
| 346 |
+
label="Your Address *",
|
| 347 |
+
placeholder="e.g., '123 Main St, City'"
|
| 348 |
+
)
|
| 349 |
+
|
| 350 |
+
borrow_output = gr.Textbox(label="Result", interactive=False)
|
| 351 |
+
borrow_btn = gr.Button("π Borrow Books")
|
| 352 |
+
refresh_borrow_btn = gr.Button("π Refresh Book List")
|
| 353 |
+
|
| 354 |
+
borrow_btn.click(
|
| 355 |
+
borrow_book,
|
| 356 |
+
inputs=[borrow_select, borrow_copies, borrower_name, borrower_phone, borrower_address],
|
| 357 |
+
outputs=borrow_output
|
| 358 |
+
)
|
| 359 |
+
|
| 360 |
+
def refresh_borrow_list():
|
| 361 |
+
titles = book_titles()
|
| 362 |
+
return gr.Dropdown.update(
|
| 363 |
+
choices=titles if titles else ["No books available"],
|
| 364 |
+
value=titles[0] if titles else None
|
| 365 |
+
)
|
| 366 |
+
|
| 367 |
+
refresh_borrow_btn.click(refresh_borrow_list, outputs=borrow_select)
|
| 368 |
+
|
| 369 |
+
# ===== TAB 4: RETURN BOOK =====
|
| 370 |
+
with gr.Tab("β©οΈ Return Book"):
|
| 371 |
+
gr.Markdown("### Return a borrowed book")
|
| 372 |
+
|
| 373 |
+
books_list = book_titles()
|
| 374 |
+
|
| 375 |
+
with gr.Row():
|
| 376 |
+
return_select = gr.Dropdown(
|
| 377 |
+
choices=books_list if books_list else ["No books available"],
|
| 378 |
+
value=books_list[0] if books_list else None,
|
| 379 |
+
label="Select Book to Return *",
|
| 380 |
+
interactive=True,
|
| 381 |
+
scale=1
|
| 382 |
+
)
|
| 383 |
+
|
| 384 |
+
returner_name = gr.Textbox(
|
| 385 |
+
label="Your Name (as per borrow record) *",
|
| 386 |
+
placeholder="e.g., 'John Doe'",
|
| 387 |
+
scale=1
|
| 388 |
+
)
|
| 389 |
+
|
| 390 |
+
return_output = gr.Textbox(label="Result", interactive=False)
|
| 391 |
+
return_btn = gr.Button("β©οΈ Return This Book")
|
| 392 |
+
refresh_return_btn = gr.Button("π Refresh Book List")
|
| 393 |
+
|
| 394 |
+
return_btn.click(
|
| 395 |
+
return_book,
|
| 396 |
+
inputs=[return_select, returner_name],
|
| 397 |
+
outputs=return_output
|
| 398 |
+
)
|
| 399 |
+
|
| 400 |
+
def refresh_return_list():
|
| 401 |
+
titles = book_titles()
|
| 402 |
+
return gr.Dropdown.update(
|
| 403 |
+
choices=titles if titles else ["No books available"],
|
| 404 |
+
value=titles[0] if titles else None
|
| 405 |
+
)
|
| 406 |
+
|
| 407 |
+
refresh_return_btn.click(refresh_return_list, outputs=return_select)
|
| 408 |
+
|
| 409 |
+
# ===== TAB 5: ADD BOOK =====
|
| 410 |
+
with gr.Tab("β Add Book"):
|
| 411 |
+
gr.Markdown("### Add a new book to the library")
|
| 412 |
+
|
| 413 |
+
with gr.Row():
|
| 414 |
+
title_input = gr.Textbox(label="Book Title *", placeholder="e.g., 'The Alchemist'", scale=1)
|
| 415 |
+
author_input = gr.Textbox(label="Author Name *", placeholder="e.g., 'Paulo Coelho'", scale=1)
|
| 416 |
+
|
| 417 |
+
copies_input = gr.Slider(label="Number of Copies *", minimum=1, maximum=100, step=1, value=1)
|
| 418 |
+
|
| 419 |
+
add_output = gr.Textbox(label="Result", interactive=False)
|
| 420 |
+
add_btn = gr.Button("β Add Book to Library")
|
| 421 |
+
|
| 422 |
+
add_btn.click(add_book, inputs=[title_input, author_input, copies_input], outputs=add_output)
|
| 423 |
+
|
| 424 |
+
# ===== TAB 6: BORROWER DETAILS =====
|
| 425 |
+
with gr.Tab("π₯ Active Borrowers"):
|
| 426 |
+
gr.Markdown("### Currently active borrowing records")
|
| 427 |
+
borrower_details = gr.Markdown(get_borrower_details())
|
| 428 |
+
|
| 429 |
+
refresh_borrowers_btn = gr.Button("π Refresh Records")
|
| 430 |
+
refresh_borrowers_btn.click(get_borrower_details, outputs=borrower_details)
|
| 431 |
+
|
| 432 |
+
# ===== TAB 7: HISTORY =====
|
| 433 |
+
with gr.Tab("π Transaction History"):
|
| 434 |
+
gr.Markdown("### All transactions (Borrow, Return, Add)")
|
| 435 |
+
history_output = gr.Textbox(
|
| 436 |
+
label="History",
|
| 437 |
+
value=get_history(),
|
| 438 |
+
interactive=False,
|
| 439 |
+
lines=20
|
| 440 |
+
)
|
| 441 |
+
|
| 442 |
+
refresh_btn = gr.Button("π Refresh History")
|
| 443 |
+
refresh_btn.click(get_history, outputs=history_output)
|
| 444 |
+
|
| 445 |
+
# Launch the app
|
| 446 |
+
if __name__ == "__main__":
|
| 447 |
+
demo.launch()
|