PraneshJs commited on
Commit
e77050b
Β·
verified Β·
1 Parent(s): 3f742dd

added app.py

Browse files
Files changed (1) hide show
  1. app.py +561 -0
app.py ADDED
@@ -0,0 +1,561 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pandas as pd
3
+ import gspread
4
+ from google.auth.transport.requests import Request
5
+ from google.oauth2.credentials import Credentials
6
+ from google_auth_oauthlib.flow import InstalledAppFlow
7
+ import json
8
+ import gradio as gr
9
+ import time
10
+ from datetime import datetime, timedelta
11
+ import threading
12
+ from dotenv import load_dotenv
13
+
14
+ # Load environment variables
15
+ load_dotenv()
16
+
17
+ # Scopes (read-only)
18
+ SCOPES = ["https://www.googleapis.com/auth/spreadsheets.readonly"]
19
+
20
+ def authorize():
21
+ creds = None
22
+
23
+ # Get JSON content from environment variables
24
+ token_json_content = os.getenv('TOKEN_JSON')
25
+ credentials_json_content = os.getenv('CREDENTIALS_JSON')
26
+
27
+ # Load token from environment variable if exists
28
+ if token_json_content:
29
+ try:
30
+ token_info = json.loads(token_json_content)
31
+ creds = Credentials.from_authorized_user_info(token_info, SCOPES)
32
+ except json.JSONDecodeError:
33
+ print("⚠️ Invalid TOKEN_JSON format in environment variable")
34
+
35
+ # If no valid credentials, start OAuth flow
36
+ if not creds or not creds.valid:
37
+ if creds and creds.expired and creds.refresh_token:
38
+ creds.refresh(Request())
39
+ else:
40
+ if not credentials_json_content:
41
+ raise ValueError("CREDENTIALS_JSON environment variable is required for OAuth flow")
42
+
43
+ try:
44
+ credentials_info = json.loads(credentials_json_content)
45
+ flow = InstalledAppFlow.from_client_config(credentials_info, SCOPES)
46
+ creds = flow.run_local_server(port=0)
47
+ except json.JSONDecodeError:
48
+ raise ValueError("Invalid CREDENTIALS_JSON format in environment variable")
49
+
50
+ # Save token back to environment (for this session only)
51
+ # Note: You may want to update your .env file manually with the new token
52
+ print("πŸ”„ New token generated. Consider updating TOKEN_JSON in your .env file with:")
53
+ print(f"TOKEN_JSON={creds.to_json()}")
54
+
55
+ return gspread.authorize(creds)
56
+
57
+ # Function to get data from a specific sheet
58
+ def get_sheet_data(spreadsheet, gid, sheet_name):
59
+ try:
60
+ sheet = spreadsheet.get_worksheet_by_id(gid)
61
+ all_values = sheet.get_all_values()
62
+
63
+ if not all_values or len(all_values) <= 4:
64
+ print(f"⚠️ {sheet_name} sheet doesn't have enough data")
65
+ return pd.DataFrame()
66
+
67
+ # Use row 4 as headers (the actual column names)
68
+ headers = all_values[4]
69
+ clean_headers = []
70
+ seen_headers = {}
71
+
72
+ for i, header in enumerate(headers):
73
+ if header.strip(): # Non-empty header
74
+ base_header = header.strip()
75
+ # Handle duplicate headers by adding a counter
76
+ if base_header in seen_headers:
77
+ seen_headers[base_header] += 1
78
+ clean_header = f"{base_header}_{seen_headers[base_header]}"
79
+ else:
80
+ seen_headers[base_header] = 0
81
+ clean_header = base_header
82
+ clean_headers.append(clean_header)
83
+ else: # Empty header
84
+ clean_headers.append(f"Empty_Col_{i}")
85
+
86
+ # Create DataFrame starting from row 5 (after headers)
87
+ data_rows = all_values[5:]
88
+ if data_rows:
89
+ df = pd.DataFrame(data_rows, columns=clean_headers)
90
+ # Remove completely empty columns
91
+ df = df.loc[:, (df != '').any(axis=0)]
92
+ print(f"βœ… Loaded {len(df)} rows from {sheet_name}")
93
+ return df
94
+ else:
95
+ print(f"⚠️ No data rows found in {sheet_name}")
96
+ return pd.DataFrame()
97
+
98
+ except Exception as e:
99
+ print(f"❌ Error loading {sheet_name}: {str(e)}")
100
+ return pd.DataFrame()
101
+
102
+ # Function to get details sheet information
103
+ def get_details_info(spreadsheet):
104
+ try:
105
+ details_sheet = spreadsheet.get_worksheet_by_id(847680829)
106
+ all_values = details_sheet.get_all_values()
107
+
108
+ if not all_values:
109
+ return None
110
+
111
+ # Use row 4 as headers
112
+ headers = all_values[4]
113
+ clean_headers = []
114
+ for i, header in enumerate(headers):
115
+ if header.strip():
116
+ clean_headers.append(header.strip())
117
+ else:
118
+ clean_headers.append(f"Empty_Col_{i}")
119
+
120
+ # Get data rows after header
121
+ data_rows = all_values[5:]
122
+
123
+ if data_rows:
124
+ df = pd.DataFrame(data_rows, columns=clean_headers)
125
+ df = df.loc[:, (df != '').any(axis=0)]
126
+
127
+ details_info = {}
128
+
129
+ # Extract specific information
130
+ for idx in range(len(df)):
131
+ student_data = df.iloc[idx]
132
+ year_value = str(student_data.get('YEAR', '')).strip()
133
+
134
+ # Get Average Reward Points
135
+ if 'AVERAGE REWARD POINT' in year_value:
136
+ details_info['average_points'] = {
137
+ 'I': student_data.get('I', ''),
138
+ 'II': student_data.get('II', ''),
139
+ 'II L': student_data.get('II L', ''),
140
+ 'III': student_data.get('III', ''),
141
+ 'IV': student_data.get('IV', '')
142
+ }
143
+
144
+ # Get IP 2 Redemption Dates
145
+ elif 'Last Day for IP 2 Redemption Duration' in str(student_data.get('Redemption Dates', '')):
146
+ details_info['ip2_redemption'] = {
147
+ 'S1': student_data.get('S1', ''),
148
+ 'S2': student_data.get('S2', ''),
149
+ 'S3': student_data.get('S3', ''),
150
+ 'S4': student_data.get('S4', ''),
151
+ 'S5': student_data.get('S5', ''),
152
+ 'S6': student_data.get('S6', ''),
153
+ 'S7': student_data.get('S7', ''),
154
+ 'S8': student_data.get('S8', '')
155
+ }
156
+
157
+ # Get IP 1 Redemption Dates
158
+ elif 'Last Day for IP 1 Redemption Duration' in str(student_data.get('Redemption Dates', '')):
159
+ details_info['ip1_redemption'] = {
160
+ 'S1': student_data.get('S1', ''),
161
+ 'S2': student_data.get('S2', ''),
162
+ 'S3': student_data.get('S3', ''),
163
+ 'S4': student_data.get('S4', ''),
164
+ 'S5': student_data.get('S5', ''),
165
+ 'S6': student_data.get('S6', ''),
166
+ 'S7': student_data.get('S7', ''),
167
+ 'S8': student_data.get('S8', '')
168
+ }
169
+
170
+ # Get Last Updated Information
171
+ elif 'POINTS LAST UPDATED' in year_value:
172
+ details_info['last_updated'] = year_value
173
+
174
+ return details_info
175
+
176
+ except Exception as e:
177
+ print(f"❌ Error loading Details Sheet: {str(e)}")
178
+ return None
179
+
180
+ # Initialize global variables
181
+ print("πŸš€ Initializing application...")
182
+ client = authorize()
183
+ SHEET_ID = os.getenv('GOOGLE_SHEET_ID', "1t5uHtrRMSXQkxrFRUudDpwuN23A6K61PhdrjDNZFaV8")
184
+ spreadsheet = client.open_by_key(SHEET_ID)
185
+
186
+ # Load data from all sheets (Original 3 + New 17 = 20 sheets total)
187
+ sheet_configs = [
188
+ # Original sheets
189
+ {"gid": 688907204, "name": "AIML"},
190
+ {"gid": 451167295, "name": "CSE"},
191
+ {"gid": 1955995189, "name": "ECE"},
192
+ # New sheets
193
+ {"gid": 821473193, "name": "Sheet_4"},
194
+ {"gid": 1798819643, "name": "Sheet_5"},
195
+ {"gid": 1057532042, "name": "Sheet_6"},
196
+ {"gid": 1848020834, "name": "Sheet_7"},
197
+ {"gid": 48570283, "name": "Sheet_8"},
198
+ {"gid": 559332743, "name": "Sheet_9"},
199
+ {"gid": 1481375682, "name": "Sheet_10"},
200
+ {"gid": 1136877763, "name": "Sheet_11"},
201
+ {"gid": 510521423, "name": "Sheet_12"},
202
+ {"gid": 1936618, "name": "Sheet_13"},
203
+ {"gid": 91989289, "name": "Sheet_14"},
204
+ {"gid": 30073516, "name": "Sheet_15"},
205
+ {"gid": 857542309, "name": "Sheet_16"},
206
+ {"gid": 790318539, "name": "Sheet_17"},
207
+ {"gid": 587090068, "name": "Sheet_18"},
208
+ {"gid": 260192612, "name": "Sheet_19"},
209
+ {"gid": 400900059, "name": "Sheet_20"}
210
+ ]
211
+
212
+ # πŸ•’ GLOBAL DATA CACHE WITH 12-HOUR AUTO-REFRESH
213
+ data_cache = {
214
+ "combined_df": None,
215
+ "details_info": None,
216
+ "last_update": None,
217
+ "cache_duration_hours": 12, # 12 hours cache
218
+ "is_loading": False
219
+ }
220
+
221
+ def load_all_data():
222
+ """Load and cache all data from Google Sheets"""
223
+ global data_cache
224
+
225
+ if data_cache["is_loading"]:
226
+ print("⏳ Data loading already in progress...")
227
+ return data_cache["combined_df"], data_cache["details_info"]
228
+
229
+ data_cache["is_loading"] = True
230
+ print(f"πŸ”„ Loading fresh data from {len(sheet_configs)} Google Sheets...")
231
+ start_time = time.time()
232
+
233
+ try:
234
+ # Load all sheet data
235
+ all_dataframes = []
236
+ for config in sheet_configs:
237
+ df = get_sheet_data(spreadsheet, config["gid"], config["name"])
238
+ if not df.empty:
239
+ df['Source_Sheet'] = config["name"]
240
+ all_dataframes.append(df)
241
+ print(f" πŸ“‹ {config['name']}: {len(df)} rows")
242
+
243
+ # Combine dataframes
244
+ if all_dataframes:
245
+ try:
246
+ combined_df = pd.concat(all_dataframes, ignore_index=True, sort=False)
247
+ print(f"βœ… Successfully combined {len(combined_df)} records from {len(all_dataframes)} sheets")
248
+ except Exception as e:
249
+ print(f"❌ Error combining dataframes: {str(e)}")
250
+ print("πŸ”„ Trying alternative approach...")
251
+
252
+ # Alternative approach: standardize columns first
253
+ standard_columns = ['SL. NO.', 'YEAR', 'ROLL NO.', 'STUDENT NAME', 'COURSE CODE',
254
+ 'DEPARTMENT', 'MENTOR NAME', 'CUMULATIVE REWARD POINTS',
255
+ 'REEDEMED POINTS', 'BALANCE POINTS', 'Source_Sheet']
256
+
257
+ standardized_dfs = []
258
+ for df in all_dataframes:
259
+ new_df = pd.DataFrame()
260
+ for col in standard_columns:
261
+ if col == 'Source_Sheet':
262
+ new_df[col] = df.get('Source_Sheet', '')
263
+ else:
264
+ # Try to find matching column
265
+ found_col = None
266
+ for df_col in df.columns:
267
+ if col.upper() in df_col.upper() or df_col.upper() in col.upper():
268
+ found_col = df_col
269
+ break
270
+
271
+ if found_col:
272
+ new_df[col] = df[found_col]
273
+ else:
274
+ new_df[col] = ''
275
+
276
+ standardized_dfs.append(new_df)
277
+
278
+ combined_df = pd.concat(standardized_dfs, ignore_index=True, sort=False)
279
+ print(f"βœ… Alternative approach successful: {len(combined_df)} records combined")
280
+ else:
281
+ combined_df = pd.DataFrame()
282
+ print("❌ No data found in any sheets")
283
+
284
+ # Load details info
285
+ details_info = get_details_info(spreadsheet)
286
+
287
+ # Update cache
288
+ data_cache["combined_df"] = combined_df
289
+ data_cache["details_info"] = details_info
290
+ data_cache["last_update"] = datetime.now()
291
+
292
+ load_time = time.time() - start_time
293
+ print(f"⏱️ Data loaded and cached in {load_time:.2f} seconds")
294
+ print(f"πŸ“Š Next auto-refresh in {data_cache['cache_duration_hours']} hours")
295
+
296
+ return combined_df, details_info
297
+
298
+ except Exception as e:
299
+ print(f"❌ Error loading data: {str(e)}")
300
+ return data_cache.get("combined_df", pd.DataFrame()), data_cache.get("details_info", None)
301
+
302
+ finally:
303
+ data_cache["is_loading"] = False
304
+
305
+ def get_cached_data():
306
+ """Get data from cache or refresh if 12 hours have passed"""
307
+ now = datetime.now()
308
+
309
+ # Check if cache is empty or expired (12 hours)
310
+ if (data_cache["last_update"] is None or
311
+ data_cache["combined_df"] is None or
312
+ (now - data_cache["last_update"]).total_seconds() > (data_cache["cache_duration_hours"] * 3600)):
313
+
314
+ print("πŸ”„ Cache expired or empty, loading fresh data...")
315
+ return load_all_data()
316
+ else:
317
+ cache_age_hours = (now - data_cache["last_update"]).total_seconds() / 3600
318
+ print(f"πŸš€ Using cached data (age: {cache_age_hours:.1f} hours)")
319
+ return data_cache["combined_df"], data_cache["details_info"]
320
+
321
+ def auto_refresh_worker():
322
+ """Background worker to auto-refresh data every 12 hours"""
323
+ while True:
324
+ try:
325
+ # Sleep for 12 hours (43200 seconds)
326
+ time.sleep(43200)
327
+ print("⏰ 12-hour auto-refresh triggered...")
328
+ load_all_data()
329
+ except Exception as e:
330
+ print(f"❌ Auto-refresh error: {str(e)}")
331
+ # If error, wait 1 hour before trying again
332
+ time.sleep(3600)
333
+
334
+ # Load initial data
335
+ print("πŸ“Š Loading initial data...")
336
+ load_all_data()
337
+
338
+ # Start background auto-refresh thread
339
+ refresh_thread = threading.Thread(target=auto_refresh_worker, daemon=True)
340
+ refresh_thread.start()
341
+ print("πŸ•’ Auto-refresh thread started (updates every 12 hours)")
342
+
343
+ # Function to search student with cached data
344
+ def search_student(roll_no):
345
+ if not roll_no.strip():
346
+ return "❌ Please enter a roll number"
347
+
348
+ # Get cached data (fast response, auto-refreshes every 12 hours)
349
+ combined_df, details_info = get_cached_data()
350
+
351
+ if combined_df.empty:
352
+ return "❌ No data available from Google Sheets"
353
+
354
+ # Look for 'ROLL NO.' column
355
+ roll_column = None
356
+ for col in combined_df.columns:
357
+ if 'roll' in col.lower() and 'no' in col.lower():
358
+ roll_column = col
359
+ break
360
+
361
+ if roll_column is None:
362
+ return f"❌ Roll number column not found. Available columns: {list(combined_df.columns)}"
363
+
364
+ student = combined_df[combined_df[roll_column].astype(str).str.strip() == roll_no.strip()]
365
+ if student.empty:
366
+ return f"❌ Roll No '{roll_no}' not found in any sheet"
367
+
368
+ record = student.iloc[0].to_dict()
369
+ student_year = str(record.get('YEAR', '')).strip()
370
+
371
+ # Format output - Simplified version
372
+ output = []
373
+ output.append("=" * 80)
374
+ output.append("STUDENT DETAILS")
375
+ output.append("=" * 80)
376
+
377
+ # Main student details
378
+ main_fields = ['ROLL NO.', 'STUDENT NAME', 'YEAR', 'DEPARTMENT', 'MENTOR NAME',
379
+ 'CUMULATIVE REWARD POINTS', 'REEDEMED POINTS', 'BALANCE POINTS']
380
+
381
+ for field in main_fields:
382
+ value = record.get(field, '')
383
+ if str(value).strip():
384
+ output.append(f"{field:<25}: {value}")
385
+
386
+ # Get student's current points (clean numeric value)
387
+ try:
388
+ student_points_str = str(record.get('CUMULATIVE REWARD POINTS', '')).replace(',', '').strip()
389
+ student_points = float(student_points_str) if student_points_str else 0
390
+ except:
391
+ student_points = 0
392
+
393
+ # Add year-specific average points and analysis
394
+ if details_info and 'average_points' in details_info:
395
+ output.append("\n" + "=" * 80)
396
+ output.append(f"AVERAGE REWARD POINTS FOR YEAR {student_year}")
397
+ output.append("=" * 80)
398
+
399
+ if student_year in details_info['average_points']:
400
+ avg_points_str = details_info['average_points'][student_year]
401
+ try:
402
+ avg_points = float(avg_points_str) if avg_points_str else 0
403
+ except:
404
+ avg_points = 0
405
+
406
+ if avg_points > 0:
407
+ output.append(f"Average Points for Year {student_year:<8}: {avg_points_str}")
408
+
409
+ # Calculate difference and provide guidance
410
+ points_difference = avg_points - student_points
411
+
412
+ if points_difference > 0:
413
+ # Student is below average
414
+ output.append(f"\n🎯 POINTS NEEDED TO REACH AVERAGE: {points_difference:.0f} points")
415
+ output.append("\nπŸ’‘ WAYS TO EARN POINTS:")
416
+ output.append(" β€’ PS Activities")
417
+ output.append(" β€’ TAC")
418
+ output.append(" β€’ Hackathons / Technical Events")
419
+ output.append(" β€’ Project Competitions")
420
+ else:
421
+ # Student is at or above average
422
+ output.append(f"\nπŸŽ‰ EXCELLENT! You are {abs(points_difference):.0f} points ABOVE the average!")
423
+ output.append(" Keep up the great work! 🌟")
424
+
425
+ # Add last updated info
426
+ if details_info and 'last_updated' in details_info:
427
+ output.append("\n" + "-" * 60)
428
+ output.append("LAST UPDATE INFO")
429
+ output.append("-" * 60)
430
+ output.append(details_info['last_updated'])
431
+
432
+ output.append("\n" + "=" * 80)
433
+
434
+ return "\n".join(output)
435
+
436
+ # Function to get system information
437
+ def get_system_info():
438
+ combined_df, details_info = get_cached_data()
439
+
440
+ if not details_info:
441
+ return "❌ No system information available"
442
+
443
+ output = []
444
+ output.append("=" * 80)
445
+ output.append("SYSTEM INFORMATION")
446
+ output.append("=" * 80)
447
+
448
+ # Average Points
449
+ if 'average_points' in details_info:
450
+ output.append("\nAVERAGE REWARD POINTS BY YEAR:")
451
+ output.append("-" * 40)
452
+ for year, points in details_info['average_points'].items():
453
+ if points:
454
+ output.append(f"Year {year:<10}: {points}")
455
+
456
+ # Redemption Dates
457
+ if 'ip1_redemption' in details_info:
458
+ output.append("\nIP 1 REDEMPTION DATES:")
459
+ output.append("-" * 40)
460
+ for semester, date in details_info['ip1_redemption'].items():
461
+ if date and date != '-':
462
+ output.append(f"{semester:<10}: {date}")
463
+
464
+ if 'ip2_redemption' in details_info:
465
+ output.append("\nIP 2 REDEMPTION DATES:")
466
+ output.append("-" * 40)
467
+ for semester, date in details_info['ip2_redemption'].items():
468
+ if date and date != '-':
469
+ output.append(f"{semester:<10}: {date}")
470
+
471
+ if 'last_updated' in details_info:
472
+ output.append(f"\nLAST UPDATED:")
473
+ output.append("-" * 40)
474
+ output.append(details_info['last_updated'])
475
+
476
+ # Cache info
477
+ if data_cache["last_update"]:
478
+ cache_age = datetime.now() - data_cache["last_update"]
479
+ hours = cache_age.total_seconds() / 3600
480
+ next_refresh_hours = 12 - hours
481
+ output.append(f"\nπŸ“Š Data age: {hours:.1f} hours")
482
+ if next_refresh_hours > 0:
483
+ output.append(f"⏰ Next auto-refresh in: {next_refresh_hours:.1f} hours")
484
+ else:
485
+ output.append("⏰ Auto-refresh due now")
486
+
487
+ output.append("\n" + "=" * 80)
488
+
489
+ return "\n".join(output)
490
+
491
+ # Create Gradio interface
492
+ with gr.Blocks(title="Student Reward Points Check", theme=gr.themes.Soft()) as app:
493
+ gr.Markdown("# πŸŽ“ Student Reward Points Check")
494
+ gr.Markdown("### Search for student details including reward points and redemption dates")
495
+ gr.Markdown("πŸ•’ **Auto-Updates**: Data automatically refreshes every 12 hours")
496
+
497
+ with gr.Tabs():
498
+ with gr.TabItem("πŸ” Student Search"):
499
+ with gr.Row():
500
+ with gr.Column(scale=3):
501
+ roll_input = gr.Textbox(
502
+ label="Enter Roll Number",
503
+ placeholder="e.g., 7376222AL181",
504
+ value=""
505
+ )
506
+ with gr.Column(scale=1):
507
+ search_btn = gr.Button("πŸ” Search Student", variant="primary")
508
+
509
+ result_output = gr.Textbox(
510
+ label="Student Details",
511
+ lines=25,
512
+ max_lines=30,
513
+ show_copy_button=True
514
+ )
515
+
516
+ with gr.TabItem("ℹ️ System Information"):
517
+ system_btn = gr.Button("πŸ“Š Get System Information", variant="secondary")
518
+ system_output = gr.Textbox(
519
+ label="System Information",
520
+ lines=20,
521
+ max_lines=25,
522
+ show_copy_button=True
523
+ )
524
+
525
+ # Event handlers
526
+ search_btn.click(fn=search_student, inputs=roll_input, outputs=result_output)
527
+ roll_input.submit(fn=search_student, inputs=roll_input, outputs=result_output)
528
+ system_btn.click(fn=get_system_info, outputs=system_output)
529
+
530
+ # Load system info on startup
531
+ app.load(fn=get_system_info, outputs=system_output)
532
+
533
+ # 🌟 FOOTER SECTION
534
+ gr.Markdown("---")
535
+ with gr.Row():
536
+ with gr.Column():
537
+ gr.Markdown(
538
+ """
539
+ <div style="text-align: center; margin-top: 20px; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; color: white;">
540
+ <h3 style="margin: 0; color: white;">πŸ’» Developed with ❀️ by</h3>
541
+ <h2 style="margin: 5px 0; color: #ffd700;">PRANESH S</h2>
542
+ <div style="margin: 15px 0;">
543
+ <a href="https://github.com/Pranesh-2005" target="_blank" style="color: #ffd700; text-decoration: none; margin: 0 10px; font-size: 16px;">
544
+ 🐱 GitHub
545
+ </a>
546
+ <span style="color: #ffd700;">|</span>
547
+ <a href="https://www.linkedin.com/in/pranesh5264/" target="_blank" style="color: #ffd700; text-decoration: none; margin: 0 10px; font-size: 16px;">
548
+ πŸ’Ό LinkedIn
549
+ </a>
550
+ </div>
551
+ <p style="margin: 10px 0; font-style: italic; color: #e0e0e0;">Made with πŸ’ Love and Support</p>
552
+ <p style="margin: 5px 0; font-size: 14px; color: #b0b0b0;">πŸš€ Empowering students with instant reward points tracking</p>
553
+ </div>
554
+ """,
555
+ elem_id="footer"
556
+ )
557
+
558
+ # Launch the app
559
+ if __name__ == "__main__":
560
+ print("πŸš€ Launching Gradio interface...")
561
+ app.launch(share=False, debug=True,server_name="0.0.0.0",server_port=7860,pwa=True)