AjayCharit commited on
Commit
199a64b
Β·
verified Β·
1 Parent(s): cd57ee5

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +447 -0
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()