Spaces:
Running
Running
added reward points breakdown feature
Browse files
app.py
CHANGED
|
@@ -99,6 +99,23 @@ def get_sheet_data(spreadsheet, gid, sheet_name):
|
|
| 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:
|
|
@@ -180,8 +197,18 @@ def get_details_info(spreadsheet):
|
|
| 180 |
# Initialize global variables
|
| 181 |
print("π Initializing application...")
|
| 182 |
client = authorize()
|
| 183 |
-
|
| 184 |
-
spreadsheet
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 185 |
|
| 186 |
# Load data from all sheets (Original 3 + New 17 = 20 sheets total)
|
| 187 |
sheet_configs = [
|
|
@@ -211,6 +238,7 @@ sheet_configs = [
|
|
| 211 |
# π GLOBAL DATA CACHE WITH 12-HOUR AUTO-REFRESH
|
| 212 |
data_cache = {
|
| 213 |
"combined_df": None,
|
|
|
|
| 214 |
"details_info": None,
|
| 215 |
"last_update": None,
|
| 216 |
"cache_duration_hours": 12, # 12 hours cache
|
|
@@ -223,17 +251,17 @@ def load_all_data():
|
|
| 223 |
|
| 224 |
if data_cache["is_loading"]:
|
| 225 |
print("β³ Data loading already in progress...")
|
| 226 |
-
return data_cache["combined_df"], data_cache["details_info"]
|
| 227 |
|
| 228 |
data_cache["is_loading"] = True
|
| 229 |
print(f"π Loading fresh data from {len(sheet_configs)} Google Sheets...")
|
| 230 |
start_time = time.time()
|
| 231 |
|
| 232 |
try:
|
| 233 |
-
# Load all sheet data
|
| 234 |
all_dataframes = []
|
| 235 |
for config in sheet_configs:
|
| 236 |
-
df = get_sheet_data(
|
| 237 |
if not df.empty:
|
| 238 |
df['Source_Sheet'] = config["name"]
|
| 239 |
all_dataframes.append(df)
|
|
@@ -280,11 +308,15 @@ def load_all_data():
|
|
| 280 |
combined_df = pd.DataFrame()
|
| 281 |
print("β No data found in any sheets")
|
| 282 |
|
| 283 |
-
# Load
|
| 284 |
-
|
|
|
|
|
|
|
|
|
|
| 285 |
|
| 286 |
# Update cache
|
| 287 |
data_cache["combined_df"] = combined_df
|
|
|
|
| 288 |
data_cache["details_info"] = details_info
|
| 289 |
data_cache["last_update"] = datetime.now()
|
| 290 |
|
|
@@ -292,11 +324,13 @@ def load_all_data():
|
|
| 292 |
print(f"β±οΈ Data loaded and cached in {load_time:.2f} seconds")
|
| 293 |
print(f"π Next auto-refresh in {data_cache['cache_duration_hours']} hours")
|
| 294 |
|
| 295 |
-
return combined_df, details_info
|
| 296 |
|
| 297 |
except Exception as e:
|
| 298 |
print(f"β Error loading data: {str(e)}")
|
| 299 |
-
return data_cache.get("combined_df", pd.DataFrame()),
|
|
|
|
|
|
|
| 300 |
|
| 301 |
finally:
|
| 302 |
data_cache["is_loading"] = False
|
|
@@ -315,7 +349,7 @@ def get_cached_data():
|
|
| 315 |
else:
|
| 316 |
cache_age_hours = (now - data_cache["last_update"]).total_seconds() / 3600
|
| 317 |
print(f"π Using cached data (age: {cache_age_hours:.1f} hours)")
|
| 318 |
-
return data_cache["combined_df"], data_cache["details_info"]
|
| 319 |
|
| 320 |
def auto_refresh_worker():
|
| 321 |
"""Background worker to auto-refresh data every 12 hours"""
|
|
@@ -330,6 +364,80 @@ def auto_refresh_worker():
|
|
| 330 |
# If error, wait 1 hour before trying again
|
| 331 |
time.sleep(3600)
|
| 332 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 333 |
# Load initial data
|
| 334 |
print("π Loading initial data...")
|
| 335 |
load_all_data()
|
|
@@ -348,7 +456,7 @@ def search_student(roll_no):
|
|
| 348 |
roll_no = roll_no.strip().upper()
|
| 349 |
|
| 350 |
# Get cached data (fast response, auto-refreshes every 12 hours)
|
| 351 |
-
combined_df, details_info = get_cached_data()
|
| 352 |
|
| 353 |
if combined_df.empty:
|
| 354 |
return "β No data available from Google Sheets"
|
|
@@ -366,14 +474,14 @@ def search_student(roll_no):
|
|
| 366 |
# Convert the roll numbers in DataFrame to uppercase for comparison
|
| 367 |
student = combined_df[combined_df[roll_column].astype(str).str.strip().str.upper() == roll_no]
|
| 368 |
if student.empty:
|
| 369 |
-
return f"β Roll No '{roll_no}' not found in any sheet"
|
| 370 |
|
| 371 |
-
|
| 372 |
record = student.iloc[0].to_dict()
|
|
|
|
| 373 |
student_year = str(record.get('YEAR', '')).strip()
|
| 374 |
|
| 375 |
-
# Log to see which roll number is
|
| 376 |
-
print(f"Roll No Searched: {roll_no}
|
| 377 |
|
| 378 |
# Format output - Simplified version
|
| 379 |
output = []
|
|
@@ -424,10 +532,17 @@ def search_student(roll_no):
|
|
| 424 |
output.append(" β’ TAC")
|
| 425 |
output.append(" β’ Hackathons / Technical Events")
|
| 426 |
output.append(" β’ Project Competitions")
|
|
|
|
| 427 |
else:
|
| 428 |
# Student is at or above average
|
| 429 |
output.append(f"\nπ EXCELLENT! You are {abs(points_difference):.0f} points ABOVE the average!")
|
| 430 |
output.append(" Keep up the great work! π")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 431 |
|
| 432 |
# Add last updated info
|
| 433 |
if details_info and 'last_updated' in details_info:
|
|
@@ -436,13 +551,24 @@ def search_student(roll_no):
|
|
| 436 |
output.append("-" * 60)
|
| 437 |
output.append(details_info['last_updated'])
|
| 438 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 439 |
output.append("\n" + "=" * 80)
|
| 440 |
|
| 441 |
return "\n".join(output)
|
| 442 |
|
| 443 |
# Function to get system information
|
| 444 |
def get_system_info():
|
| 445 |
-
combined_df, details_info = get_cached_data()
|
| 446 |
|
| 447 |
if not details_info:
|
| 448 |
return "β No system information available"
|
|
@@ -515,8 +641,8 @@ with gr.Blocks(title="Student Reward Points Check", theme=gr.themes.Soft()) as a
|
|
| 515 |
|
| 516 |
result_output = gr.Textbox(
|
| 517 |
label="Student Details",
|
| 518 |
-
lines=
|
| 519 |
-
max_lines=
|
| 520 |
show_copy_button=True,
|
| 521 |
autoscroll=False
|
| 522 |
)
|
|
|
|
| 99 |
print(f"β Error loading {sheet_name}: {str(e)}")
|
| 100 |
return pd.DataFrame()
|
| 101 |
|
| 102 |
+
# Function to get studentwise reward points data
|
| 103 |
+
def get_studentwise_data(spreadsheet):
|
| 104 |
+
try:
|
| 105 |
+
worksheet = spreadsheet.worksheet("Studentwise Reward Points")
|
| 106 |
+
all_values = worksheet.get_all_values()
|
| 107 |
+
|
| 108 |
+
if len(all_values) < 3:
|
| 109 |
+
print("β οΈ Studentwise Reward Points sheet doesn't have enough data")
|
| 110 |
+
return None
|
| 111 |
+
|
| 112 |
+
print(f"β
Loaded {len(all_values)} rows from Studentwise Reward Points")
|
| 113 |
+
return all_values
|
| 114 |
+
|
| 115 |
+
except Exception as e:
|
| 116 |
+
print(f"β Error loading Studentwise Reward Points: {str(e)}")
|
| 117 |
+
return None
|
| 118 |
+
|
| 119 |
# Function to get details sheet information
|
| 120 |
def get_details_info(spreadsheet):
|
| 121 |
try:
|
|
|
|
| 197 |
# Initialize global variables
|
| 198 |
print("π Initializing application...")
|
| 199 |
client = authorize()
|
| 200 |
+
|
| 201 |
+
# π§ Get spreadsheet IDs from environment variables
|
| 202 |
+
MAIN_SHEET_ID = os.getenv('GOOGLE_SHEET_ID') # Your main sheets (20 sheets)
|
| 203 |
+
STUDENTWISE_SHEET_ID = os.getenv('STUDENTWISE_SHEET_ID') # Studentwise Reward Points sheet
|
| 204 |
+
|
| 205 |
+
if not MAIN_SHEET_ID:
|
| 206 |
+
raise ValueError("GOOGLE_SHEET_ID environment variable is required")
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
# Open both spreadsheets
|
| 210 |
+
main_spreadsheet = client.open_by_key(MAIN_SHEET_ID)
|
| 211 |
+
studentwise_spreadsheet = client.open_by_key(STUDENTWISE_SHEET_ID)
|
| 212 |
|
| 213 |
# Load data from all sheets (Original 3 + New 17 = 20 sheets total)
|
| 214 |
sheet_configs = [
|
|
|
|
| 238 |
# π GLOBAL DATA CACHE WITH 12-HOUR AUTO-REFRESH
|
| 239 |
data_cache = {
|
| 240 |
"combined_df": None,
|
| 241 |
+
"studentwise_data": None,
|
| 242 |
"details_info": None,
|
| 243 |
"last_update": None,
|
| 244 |
"cache_duration_hours": 12, # 12 hours cache
|
|
|
|
| 251 |
|
| 252 |
if data_cache["is_loading"]:
|
| 253 |
print("β³ Data loading already in progress...")
|
| 254 |
+
return data_cache["combined_df"], data_cache["studentwise_data"], data_cache["details_info"]
|
| 255 |
|
| 256 |
data_cache["is_loading"] = True
|
| 257 |
print(f"π Loading fresh data from {len(sheet_configs)} Google Sheets...")
|
| 258 |
start_time = time.time()
|
| 259 |
|
| 260 |
try:
|
| 261 |
+
# Load all sheet data from main spreadsheet
|
| 262 |
all_dataframes = []
|
| 263 |
for config in sheet_configs:
|
| 264 |
+
df = get_sheet_data(main_spreadsheet, config["gid"], config["name"])
|
| 265 |
if not df.empty:
|
| 266 |
df['Source_Sheet'] = config["name"]
|
| 267 |
all_dataframes.append(df)
|
|
|
|
| 308 |
combined_df = pd.DataFrame()
|
| 309 |
print("β No data found in any sheets")
|
| 310 |
|
| 311 |
+
# Load studentwise reward points data from separate spreadsheet
|
| 312 |
+
studentwise_data = get_studentwise_data(studentwise_spreadsheet)
|
| 313 |
+
|
| 314 |
+
# Load details info from main spreadsheet
|
| 315 |
+
details_info = get_details_info(main_spreadsheet)
|
| 316 |
|
| 317 |
# Update cache
|
| 318 |
data_cache["combined_df"] = combined_df
|
| 319 |
+
data_cache["studentwise_data"] = studentwise_data
|
| 320 |
data_cache["details_info"] = details_info
|
| 321 |
data_cache["last_update"] = datetime.now()
|
| 322 |
|
|
|
|
| 324 |
print(f"β±οΈ Data loaded and cached in {load_time:.2f} seconds")
|
| 325 |
print(f"π Next auto-refresh in {data_cache['cache_duration_hours']} hours")
|
| 326 |
|
| 327 |
+
return combined_df, studentwise_data, details_info
|
| 328 |
|
| 329 |
except Exception as e:
|
| 330 |
print(f"β Error loading data: {str(e)}")
|
| 331 |
+
return (data_cache.get("combined_df", pd.DataFrame()),
|
| 332 |
+
data_cache.get("studentwise_data", None),
|
| 333 |
+
data_cache.get("details_info", None))
|
| 334 |
|
| 335 |
finally:
|
| 336 |
data_cache["is_loading"] = False
|
|
|
|
| 349 |
else:
|
| 350 |
cache_age_hours = (now - data_cache["last_update"]).total_seconds() / 3600
|
| 351 |
print(f"π Using cached data (age: {cache_age_hours:.1f} hours)")
|
| 352 |
+
return data_cache["combined_df"], data_cache["studentwise_data"], data_cache["details_info"]
|
| 353 |
|
| 354 |
def auto_refresh_worker():
|
| 355 |
"""Background worker to auto-refresh data every 12 hours"""
|
|
|
|
| 364 |
# If error, wait 1 hour before trying again
|
| 365 |
time.sleep(3600)
|
| 366 |
|
| 367 |
+
def get_detailed_student_points(roll_no, studentwise_data):
|
| 368 |
+
"""Get detailed points breakdown from studentwise data"""
|
| 369 |
+
if not studentwise_data or len(studentwise_data) < 3:
|
| 370 |
+
return ""
|
| 371 |
+
|
| 372 |
+
headers = studentwise_data[0]
|
| 373 |
+
student_found = None
|
| 374 |
+
for row in studentwise_data[2:]:
|
| 375 |
+
if len(row) > 1 and row[1].strip().upper() == roll_no.strip().upper():
|
| 376 |
+
student_found = row
|
| 377 |
+
break
|
| 378 |
+
if not student_found:
|
| 379 |
+
return ""
|
| 380 |
+
|
| 381 |
+
student_data = {}
|
| 382 |
+
for i, header in enumerate(headers):
|
| 383 |
+
student_data[header] = student_found[i] if i < len(student_found) else ""
|
| 384 |
+
|
| 385 |
+
output = []
|
| 386 |
+
output.append("\n")
|
| 387 |
+
output.append("=" * 80)
|
| 388 |
+
output.append("π REWARD POINTS BREAKDOWN")
|
| 389 |
+
output.append("=" * 80)
|
| 390 |
+
|
| 391 |
+
# Using a different approach - no column headers, just data with clear labels
|
| 392 |
+
categories = [
|
| 393 |
+
("INITIAL POINTS / CARRY-OVER", "-", "Initial Points"),
|
| 394 |
+
("TECHNICAL EVENTS", "Technical Events Count", "Technical Events Points"),
|
| 395 |
+
("SKILLS", "Skill Count", "Skill Points"),
|
| 396 |
+
("ASSIGNMENTS", "Assignement Count", "Assignment Points"),
|
| 397 |
+
("INTERVIEW", "Interview Count", "Interview Points"),
|
| 398 |
+
("TECHNICAL SOCIETY ACTIVITIES", "TECHNICAL SOCIETY ACTIVITIES Count", "TECHNICAL SOCIETY ACTIVITIES Points"),
|
| 399 |
+
("P SKILL", "P Skill Count", "P Skill Points"),
|
| 400 |
+
("TAC", "TAC Count", "TAC Points"),
|
| 401 |
+
("SPECIAL LAB INITIATIVES", "Special Lab Initiatives Count", "Special Lab Initiatives Points"),
|
| 402 |
+
("EXTRA-CURRICULAR ACTIVITIES", "EXTRA-CURRICULAR ACTIVITIES COUNT", "EXTRA-CURRICULAR ACTIVITIES POINTS"),
|
| 403 |
+
("STUDENT INITIATIVES", "STUDENT INITIATIVES COUNT", "STUDENT INITIATIVES POINTS"),
|
| 404 |
+
("EXTERNAL EVENTS", "EXTERNAL EVENTS COUNT", "EXTERNAL EVENTS POINTS"),
|
| 405 |
+
("TOTAL (2023-2024 EVEN)", "Total Count", "Total Points"),
|
| 406 |
+
("PENALTIES", "Negative Count", "Negative Points"),
|
| 407 |
+
("CUMULATIVE POINTS", "-", "Cumulative Points"),
|
| 408 |
+
("INNOVATIVE PRACTICE - 1 (IP-1)", "-", "IP 1 R"),
|
| 409 |
+
("INNOVATIVE PRACTICE - 2 (IP-2)", "-", "IP 2 R"),
|
| 410 |
+
("REDEEMED POINTS", "-", "Redeemed Points"),
|
| 411 |
+
("BALANCE POINTS", "-", "Balance Points"),
|
| 412 |
+
("CARRY FORWARD TO NEXT SEMESTER", "-", "EL. CA. FR. POINTS")
|
| 413 |
+
]
|
| 414 |
+
|
| 415 |
+
total_earned = 0
|
| 416 |
+
total_redeemed = 0
|
| 417 |
+
|
| 418 |
+
for idx, (category_name, count_key, points_key) in enumerate(categories):
|
| 419 |
+
count_val = "-" if count_key == "-" else student_data.get(count_key, "0")
|
| 420 |
+
points_val = student_data.get(points_key, "0.00")
|
| 421 |
+
|
| 422 |
+
try:
|
| 423 |
+
if points_val and points_val != "-":
|
| 424 |
+
points_float = float(str(points_val).replace(',', ''))
|
| 425 |
+
points_val = f"{points_float:.2f}"
|
| 426 |
+
if points_key == "Cumulative Points":
|
| 427 |
+
total_earned = points_float
|
| 428 |
+
elif points_key == "Redeemed Points":
|
| 429 |
+
total_redeemed = points_float
|
| 430 |
+
except:
|
| 431 |
+
pass
|
| 432 |
+
|
| 433 |
+
# Alternative format - more readable
|
| 434 |
+
output.append(f"π **{category_name}**")
|
| 435 |
+
output.append(f" Count: {count_val} | Points: {points_val}")
|
| 436 |
+
|
| 437 |
+
output.append("=" * 80)
|
| 438 |
+
|
| 439 |
+
return "\n".join(output)
|
| 440 |
+
|
| 441 |
# Load initial data
|
| 442 |
print("π Loading initial data...")
|
| 443 |
load_all_data()
|
|
|
|
| 456 |
roll_no = roll_no.strip().upper()
|
| 457 |
|
| 458 |
# Get cached data (fast response, auto-refreshes every 12 hours)
|
| 459 |
+
combined_df, studentwise_data, details_info = get_cached_data()
|
| 460 |
|
| 461 |
if combined_df.empty:
|
| 462 |
return "β No data available from Google Sheets"
|
|
|
|
| 474 |
# Convert the roll numbers in DataFrame to uppercase for comparison
|
| 475 |
student = combined_df[combined_df[roll_column].astype(str).str.strip().str.upper() == roll_no]
|
| 476 |
if student.empty:
|
| 477 |
+
return f"β Roll No '{roll_no}' not found in any sheet"
|
| 478 |
|
|
|
|
| 479 |
record = student.iloc[0].to_dict()
|
| 480 |
+
student_name = str(record.get('STUDENT NAME', 'Unknown')).strip()
|
| 481 |
student_year = str(record.get('YEAR', '')).strip()
|
| 482 |
|
| 483 |
+
# Log to see which roll number and student name is searched by user
|
| 484 |
+
print(f"Roll No Searched: {roll_no} | Student Name: {student_name}")
|
| 485 |
|
| 486 |
# Format output - Simplified version
|
| 487 |
output = []
|
|
|
|
| 532 |
output.append(" β’ TAC")
|
| 533 |
output.append(" β’ Hackathons / Technical Events")
|
| 534 |
output.append(" β’ Project Competitions")
|
| 535 |
+
output.append(" β’ Refer Reward points Breakdown for more details")
|
| 536 |
else:
|
| 537 |
# Student is at or above average
|
| 538 |
output.append(f"\nπ EXCELLENT! You are {abs(points_difference):.0f} points ABOVE the average!")
|
| 539 |
output.append(" Keep up the great work! π")
|
| 540 |
+
output.append(" Refer Reward points Breakdown for more details")
|
| 541 |
+
|
| 542 |
+
# Add detailed points breakdown from studentwise data
|
| 543 |
+
detailed_points = get_detailed_student_points(roll_no, studentwise_data)
|
| 544 |
+
if detailed_points:
|
| 545 |
+
output.append(detailed_points)
|
| 546 |
|
| 547 |
# Add last updated info
|
| 548 |
if details_info and 'last_updated' in details_info:
|
|
|
|
| 551 |
output.append("-" * 60)
|
| 552 |
output.append(details_info['last_updated'])
|
| 553 |
|
| 554 |
+
# Show cache info
|
| 555 |
+
if data_cache["last_update"]:
|
| 556 |
+
cache_age = datetime.now() - data_cache["last_update"]
|
| 557 |
+
hours = cache_age.total_seconds() / 3600
|
| 558 |
+
next_refresh_hours = 12 - hours
|
| 559 |
+
output.append(f"\nπ Data age: {hours:.1f} hours")
|
| 560 |
+
if next_refresh_hours > 0:
|
| 561 |
+
output.append(f"β° Next auto-refresh in: {next_refresh_hours:.1f} hours")
|
| 562 |
+
else:
|
| 563 |
+
output.append("β° Auto-refresh due now")
|
| 564 |
+
|
| 565 |
output.append("\n" + "=" * 80)
|
| 566 |
|
| 567 |
return "\n".join(output)
|
| 568 |
|
| 569 |
# Function to get system information
|
| 570 |
def get_system_info():
|
| 571 |
+
combined_df, studentwise_data, details_info = get_cached_data()
|
| 572 |
|
| 573 |
if not details_info:
|
| 574 |
return "β No system information available"
|
|
|
|
| 641 |
|
| 642 |
result_output = gr.Textbox(
|
| 643 |
label="Student Details",
|
| 644 |
+
lines=50,
|
| 645 |
+
max_lines=60,
|
| 646 |
show_copy_button=True,
|
| 647 |
autoscroll=False
|
| 648 |
)
|