PraneshJs commited on
Commit
f85354a
·
verified ·
1 Parent(s): 875c1e6

added IP details sections to view about Innovative Practice Details

Browse files
Files changed (1) hide show
  1. app.py +282 -17
app.py CHANGED
@@ -58,6 +58,244 @@ def authorize():
58
 
59
  return gspread.authorize(creds)
60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
  # Function to get data from a specific sheet
62
  def get_sheet_data(spreadsheet, gid, sheet_name):
63
  try:
@@ -674,6 +912,7 @@ def details_sheet_watcher():
674
 
675
  time.sleep(check_interval)
676
 
 
677
  def get_detailed_student_points(roll_no, studentwise_data):
678
  """Get detailed points breakdown from studentwise data"""
679
  if not studentwise_data or len(studentwise_data) < 3:
@@ -955,10 +1194,10 @@ def get_system_info():
955
  output.append("=" * 80)
956
  output.append("SYSTEM INFORMATION")
957
  output.append("=" * 80)
958
-
959
  # Average Points
960
  if 'average_points' in details_info:
961
- output.append("\n🎯 AVERAGE REWARD POINTS BY YEAR:")
962
  output.append("-" * 40)
963
  for year, points in details_info['average_points'].items():
964
  if points:
@@ -971,21 +1210,21 @@ def get_system_info():
971
 
972
  # Redemption Dates
973
  if 'ip1_redemption' in details_info:
974
- output.append("\n📅 IP 1 REDEMPTION DATES:")
975
  output.append("-" * 40)
976
  for semester, date in details_info['ip1_redemption'].items():
977
  if date and date != '-':
978
  output.append(f"{semester:<10}: {date}")
979
 
980
  if 'ip2_redemption' in details_info:
981
- output.append("\n📅 IP 2 REDEMPTION DATES:")
982
  output.append("-" * 40)
983
  for semester, date in details_info['ip2_redemption'].items():
984
  if date and date != '-':
985
  output.append(f"{semester:<10}: {date}")
986
 
987
  if 'last_updated' in details_info:
988
- output.append(f"\n🕒 LAST UPDATED:")
989
  output.append("-" * 40)
990
  output.append(details_info['last_updated'])
991
 
@@ -1004,19 +1243,17 @@ def get_system_info():
1004
 
1005
  return "\n".join(output)
1006
 
1007
- # Create Gradio interface - SIMPLE LAYOUT WITHOUT ANALYTICS DISPLAY
1008
  with gr.Blocks(
1009
  title="Student Reward Points Check",
1010
  theme=gr.themes.Soft(),
1011
  ) as app:
1012
-
1013
- # Simple Header
1014
- gr.Markdown("# 🎓 Student Reward Points Check")
1015
- gr.Markdown("### Search for student details including reward points and redemption dates")
1016
  gr.Markdown("### எல்லா புகழும் இறைவனுக்கே ✝ 🕉 ☪")
 
1017
  gr.Markdown("🕒 **Auto-Updates**: Data automatically refreshes when there is a change in Reward Points Sheet")
1018
- gr.Markdown("### Fill this form for any Issue/Feedback: [Issue/Feedback Form](https://docs.google.com/forms/d/e/1FAIpQLScnl0udcN2pUDENHl45HIj5HZbvDuwZ0g2eepBbp8tJYg-NvQ/viewform)")
1019
-
1020
  with gr.Tabs():
1021
  with gr.TabItem("🔍 Student Search"):
1022
  with gr.Row():
@@ -1036,6 +1273,25 @@ with gr.Blocks(
1036
  show_copy_button=True,
1037
  autoscroll=False
1038
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1039
 
1040
  with gr.TabItem("ℹ️ System Information"):
1041
  system_btn = gr.Button("📊 Get System Information", variant="secondary")
@@ -1049,9 +1305,14 @@ with gr.Blocks(
1049
  show_label=True
1050
  )
1051
 
1052
- # Event handlers - simplified without analytics updates
1053
  search_btn.click(fn=search_student, inputs=roll_input, outputs=result_output)
1054
  roll_input.submit(fn=search_student, inputs=roll_input, outputs=result_output)
 
 
 
 
 
1055
  system_btn.click(fn=get_system_info, outputs=system_output)
1056
 
1057
  # Footer section
@@ -1062,7 +1323,9 @@ with gr.Blocks(
1062
  """
1063
  <div style="text-align: center; margin-top: 20px; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; color: white;">
1064
  <h3 style="margin: 0; color: white;">💻 Developed with ❤️ by</h3>
1065
- <h2 style="margin: 5px 0; color: #ffd700;">PRANESH S</h2>
 
 
1066
  <div style="margin: 15px 0;">
1067
  <a href="https://github.com/Pranesh-2005" target="_blank" style="color: #ffd700; text-decoration: none; margin: 0 10px; font-size: 16px;">
1068
  🐱 GitHub
@@ -1082,7 +1345,8 @@ with gr.Blocks(
1082
  """,
1083
  elem_id="footer"
1084
  )
1085
- # FIXED: System info initialization function
 
1086
  def initialize_system_info():
1087
  """Initialize system information display"""
1088
  try:
@@ -1090,12 +1354,13 @@ with gr.Blocks(
1090
  except Exception as e:
1091
  print(f"⚠️ Error initializing system info: {str(e)}")
1092
  return "⚠️ System information will be available after data loads completely."
1093
-
1094
- # Load system info on startup (separate from admin mode)
1095
  app.load(
1096
  fn=initialize_system_info,
1097
  outputs=system_output
1098
  )
 
1099
  # Launch the app
1100
  if __name__ == "__main__":
1101
  print("🚀 Launching Gradio interface...")
 
58
 
59
  return gspread.authorize(creds)
60
 
61
+ # NEW FUNCTION: Extract subjects and marks
62
+ def extract_subjects_and_marks_for_gradio(roll_no):
63
+ """
64
+ Extract subjects with their redeemed points and marks for Gradio interface
65
+ Uses cached studentwise_data instead of making fresh API calls
66
+ """
67
+ if not roll_no.strip():
68
+ return "❌ Please enter a roll number"
69
+
70
+ try:
71
+ # Get cached data instead of making API calls
72
+ combined_df, studentwise_data, details_info, reward_points_df = get_cached_data()
73
+
74
+ if not studentwise_data or len(studentwise_data) < 2:
75
+ return "❌ Studentwise data not available in cache"
76
+
77
+ headers = studentwise_data[0]
78
+ roll_no = roll_no.strip().upper()
79
+
80
+ # Find the student row using cached data
81
+ student_row = None
82
+ for row in studentwise_data[1:]: # Skip header row
83
+ # Check multiple possible roll number columns (0, 1, 2)
84
+ for col_idx in [0, 1, 2]:
85
+ if col_idx < len(row) and row[col_idx].strip().upper() == roll_no:
86
+ student_row = row
87
+ break
88
+ if student_row:
89
+ break
90
+
91
+ if not student_row:
92
+ return f"❌ Student with Roll No '{roll_no}' not found."
93
+
94
+ def get_value_if_not_empty(col_name):
95
+ """Helper function to get value only if it's not empty"""
96
+ if col_name in headers:
97
+ idx = headers.index(col_name)
98
+ value = student_row[idx] if idx < len(student_row) else ''
99
+ return value.strip() if value.strip() else None
100
+ return None
101
+
102
+ def get_numeric_value(col_name):
103
+ """Helper function to get numeric value, return 0 if empty or invalid"""
104
+ value = get_value_if_not_empty(col_name)
105
+ try:
106
+ return float(value) if value else 0.0
107
+ except (ValueError, TypeError):
108
+ return 0.0
109
+
110
+ # Get student basic info
111
+ student_name = get_value_if_not_empty("Student Name") or "Unknown"
112
+
113
+ # Collect theory subjects (optimized loops)
114
+ theory_subjects = []
115
+ for i in range(1, 10):
116
+ subject_code = get_value_if_not_empty(f"TS{i}")
117
+ if subject_code:
118
+ theory_subjects.append({
119
+ 'code': subject_code,
120
+ 'ip1_points': get_numeric_value(f"IP1TS{i}R"),
121
+ 'ip2_points': get_numeric_value(f"IP2TS{i}R"),
122
+ 'ip1_marks': get_numeric_value(f"IP1TS{i}M"),
123
+ 'ip2_marks': get_numeric_value(f"IP2TS{i}M")
124
+ })
125
+
126
+ # Collect lab subjects (optimized loops)
127
+ lab_subjects = []
128
+ for i in range(1, 3):
129
+ subject_code = get_value_if_not_empty(f"LS{i}")
130
+ if subject_code:
131
+ lab_subjects.append({
132
+ 'code': subject_code,
133
+ 'ip1_points': get_numeric_value(f"IP1LS{i}R"),
134
+ 'ip2_points': get_numeric_value(f"IP2LS{i}R"),
135
+ 'ip1_marks': get_numeric_value(f"IP1LS{i}M"),
136
+ 'ip2_marks': get_numeric_value(f"IP2LS{i}M")
137
+ })
138
+
139
+ # Calculate totals using list comprehensions (faster)
140
+ for subject in theory_subjects + lab_subjects:
141
+ subject['total_points'] = subject['ip1_points'] + subject['ip2_points']
142
+ subject['total_marks'] = subject['ip1_marks'] + subject['ip2_marks']
143
+
144
+ # Calculate grand totals
145
+ total_theory_ip1 = sum(s['ip1_points'] for s in theory_subjects)
146
+ total_theory_ip2 = sum(s['ip2_points'] for s in theory_subjects)
147
+ total_lab_ip1 = sum(s['ip1_points'] for s in lab_subjects)
148
+ total_lab_ip2 = sum(s['ip2_points'] for s in lab_subjects)
149
+
150
+ grand_total_ip1 = total_theory_ip1 + total_lab_ip1
151
+ grand_total_ip2 = total_theory_ip2 + total_lab_ip2
152
+ grand_total_points = grand_total_ip1 + grand_total_ip2
153
+
154
+ # Calculate marks totals
155
+ total_theory_marks_ip1 = sum(s['ip1_marks'] for s in theory_subjects)
156
+ total_theory_marks_ip2 = sum(s['ip2_marks'] for s in theory_subjects)
157
+ total_lab_marks_ip1 = sum(s['ip1_marks'] for s in lab_subjects)
158
+ total_lab_marks_ip2 = sum(s['ip2_marks'] for s in lab_subjects)
159
+
160
+ grand_total_marks_ip1 = total_theory_marks_ip1 + total_lab_marks_ip1
161
+ grand_total_marks_ip2 = total_theory_marks_ip2 + total_lab_marks_ip2
162
+ grand_total_marks = grand_total_marks_ip1 + grand_total_marks_ip2
163
+
164
+ total_subjects = len(theory_subjects) + len(lab_subjects)
165
+
166
+ # SIMPLE TABLE FORMATTING WITHOUT CONFUSING SYMBOLS
167
+ def format_table_row(subject, ip1, ip2, total):
168
+ """Format a table row with clean alignment"""
169
+ # Fixed column widths
170
+ subject_col = 48
171
+ ip1_col = 10
172
+ ip2_col = 10
173
+ total_col = 12
174
+
175
+ # Format subject column (left-aligned)
176
+ subject_str = str(subject)[:subject_col].ljust(subject_col)
177
+
178
+ # Format numeric columns (right-aligned with fixed width)
179
+ ip1_str = str(ip1).rjust(ip1_col)
180
+ ip2_str = str(ip2).rjust(ip2_col)
181
+ total_str = str(total).rjust(total_col)
182
+
183
+ return f"{subject_str} {ip1_str} {ip2_str} {total_str}"
184
+
185
+ # Build output with clean formatting
186
+ output = []
187
+ output.append("")
188
+ output.append("🏆 REWARD POINTS DISTRIBUTION")
189
+ output.append("-" * 88)
190
+
191
+ # Header row
192
+ header_row = format_table_row("SUBJECTS", "IP - 1", "IP - 2", "TOTAL")
193
+ output.append(header_row)
194
+ output.append("-" * 88)
195
+
196
+ # Theory courses section
197
+ if theory_subjects:
198
+ theory_header = format_table_row(f"THEORY COURSES - ({len(theory_subjects)} COURSES)", "", "", "")
199
+ output.append(theory_header)
200
+
201
+ for subject in theory_subjects:
202
+ ip1_val = f"{subject['ip1_points']:.2f}" if subject['ip1_points'] > 0 else ""
203
+ ip2_val = f"{subject['ip2_points']:.2f}" if subject['ip2_points'] > 0 else ""
204
+ total_val = f"{subject['total_points']:.2f}"
205
+ subject_row = format_table_row(subject['code'], ip1_val, ip2_val, total_val)
206
+ output.append(subject_row)
207
+
208
+ # Add-on courses
209
+ addon_row = format_table_row("ADD-ON / HONOR / MINOR COURSES", "", "", "")
210
+ output.append(addon_row)
211
+
212
+ # Lab courses section
213
+ if lab_subjects:
214
+ lab_header = format_table_row(f"LAB. COURSES - ({len(lab_subjects)} COURSES)", "", "", "")
215
+ output.append(lab_header)
216
+
217
+ for subject in lab_subjects:
218
+ ip1_val = f"{subject['ip1_points']:.2f}" if subject['ip1_points'] > 0 else ""
219
+ ip2_val = f"{subject['ip2_points']:.2f}" if subject['ip2_points'] > 0 else ""
220
+ total_val = f"{subject['total_points']:.2f}"
221
+ subject_row = format_table_row(subject['code'], ip1_val, ip2_val, total_val)
222
+ output.append(subject_row)
223
+ else:
224
+ lab_header = format_table_row("LAB. COURSES - (0 COURSES)", "", "", "")
225
+ output.append(lab_header)
226
+
227
+ # Total row
228
+ grand_ip1_val = f"{grand_total_ip1:,.2f}" if grand_total_ip1 > 0 else "0.00"
229
+ grand_ip2_val = f"{grand_total_ip2:,.2f}" if grand_total_ip2 > 0 else "0.00"
230
+ grand_total_val = f"{grand_total_points:,.2f}"
231
+ total_row = format_table_row(f"TOTAL - ({total_subjects} SUBJECTS)", grand_ip1_val, grand_ip2_val, grand_total_val)
232
+ output.append("-" * 88)
233
+ output.append(total_row)
234
+
235
+ output.append("")
236
+
237
+ # INTERNAL MARKS DISTRIBUTION
238
+ output.append("🎯 INTERNAL MARKS DISTRIBUTION")
239
+ output.append("-" * 88)
240
+
241
+ # Header for marks
242
+ marks_header = format_table_row("SUBJECTS", "IP - 1", "IP - 2", "TOTAL")
243
+ output.append(marks_header)
244
+ output.append("-" * 88)
245
+
246
+ # Theory courses with marks
247
+ if theory_subjects:
248
+ theory_marks_header = format_table_row(f"THEORY COURSES - ({len(theory_subjects)} COURSES)", "", "", "")
249
+ output.append(theory_marks_header)
250
+
251
+ for subject in theory_subjects:
252
+ ip1_marks = f"{subject['ip1_marks']:.2f}" if subject['ip1_marks'] > 0 else ""
253
+ ip2_marks = f"{subject['ip2_marks']:.2f}" if subject['ip2_marks'] > 0 else ""
254
+ total_marks = f"{subject['total_marks']:.2f}"
255
+ marks_row = format_table_row(subject['code'], ip1_marks, ip2_marks, total_marks)
256
+ output.append(marks_row)
257
+
258
+ # Add-on courses with marks
259
+ addon_marks_row = format_table_row("ADD-ON / HONOR / MINOR COURSES", "", "", "")
260
+ output.append(addon_marks_row)
261
+
262
+ # Lab courses with marks
263
+ if lab_subjects:
264
+ lab_marks_header = format_table_row(f"LAB. COURSES - ({len(lab_subjects)} COURSES)", "", "", "")
265
+ output.append(lab_marks_header)
266
+
267
+ for subject in lab_subjects:
268
+ ip1_marks = f"{subject['ip1_marks']:.2f}" if subject['ip1_marks'] > 0 else ""
269
+ ip2_marks = f"{subject['ip2_marks']:.2f}" if subject['ip2_marks'] > 0 else ""
270
+ total_marks = f"{subject['total_marks']:.2f}"
271
+ marks_row = format_table_row(subject['code'], ip1_marks, ip2_marks, total_marks)
272
+ output.append(marks_row)
273
+ else:
274
+ lab_marks_header = format_table_row("LAB. COURSES - (0 COURSES)", "", "", "")
275
+ output.append(lab_marks_header)
276
+
277
+ # Total marks row
278
+ marks_ip1_total = f"{grand_total_marks_ip1:.2f}" if grand_total_marks_ip1 > 0 else "0.00"
279
+ marks_ip2_total = f"{grand_total_marks_ip2:.2f}" if grand_total_marks_ip2 > 0 else "0.00"
280
+ marks_total_total = f"{grand_total_marks:.2f}"
281
+ marks_total_row = format_table_row(f"TOTAL - ({total_subjects} SUBJECTS)", marks_ip1_total, marks_ip2_total, marks_total_total)
282
+ output.append("-" * 88)
283
+ output.append(marks_total_row)
284
+
285
+ output.append("=" * 88)
286
+
287
+ # Log the search
288
+ now_ist = datetime.now(ist).strftime("%Y-%m-%d %H:%M:%S")
289
+ print(f"IP Details Searched - Roll No: {roll_no} | Student: {student_name} | Time (IST): {now_ist}")
290
+
291
+ return "\n".join(output)
292
+
293
+ except Exception as e:
294
+ error_msg = f"❌ Error extracting subject details: {str(e)}"
295
+ print(error_msg)
296
+ return error_msg
297
+
298
+
299
  # Function to get data from a specific sheet
300
  def get_sheet_data(spreadsheet, gid, sheet_name):
301
  try:
 
912
 
913
  time.sleep(check_interval)
914
 
915
+
916
  def get_detailed_student_points(roll_no, studentwise_data):
917
  """Get detailed points breakdown from studentwise data"""
918
  if not studentwise_data or len(studentwise_data) < 3:
 
1194
  output.append("=" * 80)
1195
  output.append("SYSTEM INFORMATION")
1196
  output.append("=" * 80)
1197
+
1198
  # Average Points
1199
  if 'average_points' in details_info:
1200
+ output.append("\nAVERAGE REWARD POINTS BY YEAR:")
1201
  output.append("-" * 40)
1202
  for year, points in details_info['average_points'].items():
1203
  if points:
 
1210
 
1211
  # Redemption Dates
1212
  if 'ip1_redemption' in details_info:
1213
+ output.append("\nIP 1 REDEMPTION DATES:")
1214
  output.append("-" * 40)
1215
  for semester, date in details_info['ip1_redemption'].items():
1216
  if date and date != '-':
1217
  output.append(f"{semester:<10}: {date}")
1218
 
1219
  if 'ip2_redemption' in details_info:
1220
+ output.append("\nIP 2 REDEMPTION DATES:")
1221
  output.append("-" * 40)
1222
  for semester, date in details_info['ip2_redemption'].items():
1223
  if date and date != '-':
1224
  output.append(f"{semester:<10}: {date}")
1225
 
1226
  if 'last_updated' in details_info:
1227
+ output.append(f"\nLAST UPDATED:")
1228
  output.append("-" * 40)
1229
  output.append(details_info['last_updated'])
1230
 
 
1243
 
1244
  return "\n".join(output)
1245
 
1246
+ # Create Gradio interface
1247
  with gr.Blocks(
1248
  title="Student Reward Points Check",
1249
  theme=gr.themes.Soft(),
1250
  ) as app:
1251
+ gr.Markdown("# 🎓 Student Reward Points Checker")
1252
+ gr.Markdown("### Search for Student Details such as Reward Points, Redemption Dates and Innovative Practice (IP) Details")
 
 
1253
  gr.Markdown("### எல்லா புகழும் இறைவனுக்கே ✝ 🕉 ☪")
1254
+ gr.Markdown("## 📝 Use Desktop Site Feature (Desktop Mode) in browser for Good UI and UX")
1255
  gr.Markdown("🕒 **Auto-Updates**: Data automatically refreshes when there is a change in Reward Points Sheet")
1256
+ gr.Markdown("### Fill this form for any Issue/Feedback: [Issue/Feedback Form](https://docs.google.com/forms/d/e/1FAIpQLScnl0udcN2pUDENHl45HIj5HZbvDuwZ0g2eepBbp8tJYg-NvQ/viewform)")
 
1257
  with gr.Tabs():
1258
  with gr.TabItem("🔍 Student Search"):
1259
  with gr.Row():
 
1273
  show_copy_button=True,
1274
  autoscroll=False
1275
  )
1276
+
1277
+ with gr.TabItem("📚 Innovative Practice (IP) Details"):
1278
+ with gr.Row():
1279
+ with gr.Column(scale=3):
1280
+ subject_roll_input = gr.Textbox(
1281
+ label="Enter Roll Number for Innovative Practice (IP) Details",
1282
+ placeholder="e.g., 7376222AL181",
1283
+ value=""
1284
+ )
1285
+ with gr.Column(scale=1):
1286
+ subject_search_btn = gr.Button("📚 Get Innovative Practice (IP) Details", variant="primary")
1287
+
1288
+ subject_output = gr.Textbox(
1289
+ label="Innovative Practice (IP) Details",
1290
+ lines=65,
1291
+ max_lines=75,
1292
+ show_copy_button=True,
1293
+ autoscroll=False
1294
+ )
1295
 
1296
  with gr.TabItem("ℹ️ System Information"):
1297
  system_btn = gr.Button("📊 Get System Information", variant="secondary")
 
1305
  show_label=True
1306
  )
1307
 
1308
+ # Event handlers
1309
  search_btn.click(fn=search_student, inputs=roll_input, outputs=result_output)
1310
  roll_input.submit(fn=search_student, inputs=roll_input, outputs=result_output)
1311
+
1312
+ # NEW: Subject details event handlers
1313
+ subject_search_btn.click(fn=extract_subjects_and_marks_for_gradio, inputs=subject_roll_input, outputs=subject_output)
1314
+ subject_roll_input.submit(fn=extract_subjects_and_marks_for_gradio, inputs=subject_roll_input, outputs=subject_output)
1315
+
1316
  system_btn.click(fn=get_system_info, outputs=system_output)
1317
 
1318
  # Footer section
 
1323
  """
1324
  <div style="text-align: center; margin-top: 20px; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; color: white;">
1325
  <h3 style="margin: 0; color: white;">💻 Developed with ❤️ by</h3>
1326
+ <a href="https://praneshjs.vercel.app/" target="_blank" style="text-decoration: none;">
1327
+ <h2 style="margin: 5px 0; color: #ffd700; cursor: pointer; transition: color 0.3s ease;">PRANESH S</h2>
1328
+ </a>
1329
  <div style="margin: 15px 0;">
1330
  <a href="https://github.com/Pranesh-2005" target="_blank" style="color: #ffd700; text-decoration: none; margin: 0 10px; font-size: 16px;">
1331
  🐱 GitHub
 
1345
  """,
1346
  elem_id="footer"
1347
  )
1348
+
1349
+ # System info initialization function
1350
  def initialize_system_info():
1351
  """Initialize system information display"""
1352
  try:
 
1354
  except Exception as e:
1355
  print(f"⚠️ Error initializing system info: {str(e)}")
1356
  return "⚠️ System information will be available after data loads completely."
1357
+
1358
+ # Load system info on startup
1359
  app.load(
1360
  fn=initialize_system_info,
1361
  outputs=system_output
1362
  )
1363
+
1364
  # Launch the app
1365
  if __name__ == "__main__":
1366
  print("🚀 Launching Gradio interface...")