Spaces:
Running
Running
added auto refresh based on sheet reload
Browse files
app.py
CHANGED
|
@@ -573,6 +573,119 @@ def auto_refresh_worker():
|
|
| 573 |
# If error, wait 1 hour before trying again
|
| 574 |
time.sleep(3600)
|
| 575 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 576 |
def get_detailed_student_points(roll_no, studentwise_data):
|
| 577 |
"""Get detailed points breakdown from studentwise data"""
|
| 578 |
if not studentwise_data or len(studentwise_data) < 3:
|
|
@@ -714,6 +827,11 @@ refresh_thread = threading.Thread(target=auto_refresh_worker, daemon=True)
|
|
| 714 |
refresh_thread.start()
|
| 715 |
print("π Auto-refresh thread started (updates every 12 hours)")
|
| 716 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 717 |
# Function to search student with cached data
|
| 718 |
def search_student(roll_no):
|
| 719 |
if not roll_no.strip():
|
|
@@ -978,8 +1096,8 @@ def check_admin_mode(request: gr.Request) -> bool:
|
|
| 978 |
with gr.Blocks(title="Student Reward Points Check", theme=gr.themes.Soft()) as app:
|
| 979 |
gr.Markdown("# π Student Reward Points Check")
|
| 980 |
gr.Markdown("### Search for student details including reward points and redemption dates")
|
| 981 |
-
gr.Markdown("π **Auto-Updates**: Data automatically refreshes
|
| 982 |
-
|
| 983 |
# Store admin components for conditional visibility
|
| 984 |
admin_components = gr.State(None)
|
| 985 |
|
|
|
|
| 573 |
# If error, wait 1 hour before trying again
|
| 574 |
time.sleep(3600)
|
| 575 |
|
| 576 |
+
# Auto Refersh When Time changes is found
|
| 577 |
+
# Replace the details_sheet_watcher function (lines 577-610)
|
| 578 |
+
|
| 579 |
+
def details_sheet_watcher():
|
| 580 |
+
"""Background watcher: checks every 1 minute if 'POINTS LAST UPDATED' changed - FIXED TOKEN SPAM & THREADING"""
|
| 581 |
+
last_seen_update = None
|
| 582 |
+
consecutive_errors = 0
|
| 583 |
+
max_errors = 3
|
| 584 |
+
|
| 585 |
+
# Persistent connection variables to prevent token regeneration
|
| 586 |
+
watcher_client = None
|
| 587 |
+
watcher_spreadsheet = None
|
| 588 |
+
last_connection_time = None
|
| 589 |
+
connection_duration = 2700 # 45 minutes in seconds
|
| 590 |
+
|
| 591 |
+
print("π Starting optimized details sheet watcher (checks every 1 minute)...")
|
| 592 |
+
|
| 593 |
+
while True:
|
| 594 |
+
try:
|
| 595 |
+
# Skip if data is currently being loaded to prevent conflicts
|
| 596 |
+
if data_cache["is_loading"]:
|
| 597 |
+
print("β³ Watcher: Skipping check - data loading in progress")
|
| 598 |
+
time.sleep(60)
|
| 599 |
+
continue
|
| 600 |
+
|
| 601 |
+
# Create/refresh connection only when needed (every 45 minutes)
|
| 602 |
+
current_time = datetime.now()
|
| 603 |
+
if (watcher_client is None or
|
| 604 |
+
watcher_spreadsheet is None or
|
| 605 |
+
last_connection_time is None or
|
| 606 |
+
(current_time - last_connection_time).total_seconds() > connection_duration):
|
| 607 |
+
|
| 608 |
+
try:
|
| 609 |
+
print("π Watcher: Refreshing connection...")
|
| 610 |
+
watcher_client = authorize()
|
| 611 |
+
watcher_spreadsheet = watcher_client.open_by_key(os.getenv('GOOGLE_SHEET_ID'))
|
| 612 |
+
last_connection_time = current_time
|
| 613 |
+
print("β
Watcher: Connection refreshed")
|
| 614 |
+
except Exception as auth_error:
|
| 615 |
+
print(f"β οΈ Watcher connection error: {str(auth_error)[:100]}...")
|
| 616 |
+
consecutive_errors += 1
|
| 617 |
+
watcher_client = None
|
| 618 |
+
watcher_spreadsheet = None
|
| 619 |
+
time.sleep(60)
|
| 620 |
+
continue
|
| 621 |
+
|
| 622 |
+
# Use persistent connection to get details info
|
| 623 |
+
try:
|
| 624 |
+
details_info = get_details_info(watcher_spreadsheet)
|
| 625 |
+
except Exception as sheet_error:
|
| 626 |
+
print(f"β οΈ Watcher sheet error: {str(sheet_error)[:100]}...")
|
| 627 |
+
consecutive_errors += 1
|
| 628 |
+
# Force connection refresh on next iteration
|
| 629 |
+
watcher_client = None
|
| 630 |
+
watcher_spreadsheet = None
|
| 631 |
+
time.sleep(60)
|
| 632 |
+
continue
|
| 633 |
+
|
| 634 |
+
if details_info and 'last_updated' in details_info:
|
| 635 |
+
current_update = details_info['last_updated'].strip()
|
| 636 |
+
|
| 637 |
+
if last_seen_update is None:
|
| 638 |
+
last_seen_update = current_update
|
| 639 |
+
print(f"π Watcher: Monitoring established")
|
| 640 |
+
print(f" Current: {current_update[:80]}...")
|
| 641 |
+
elif current_update != last_seen_update:
|
| 642 |
+
print(f"π CHANGE DETECTED!")
|
| 643 |
+
print(f" Old: {last_seen_update[:60]}...")
|
| 644 |
+
print(f" New: {current_update[:60]}...")
|
| 645 |
+
|
| 646 |
+
last_seen_update = current_update
|
| 647 |
+
|
| 648 |
+
# FIXED: Direct reload without creating new thread
|
| 649 |
+
if not data_cache["is_loading"]:
|
| 650 |
+
print("π Reloading data directly...")
|
| 651 |
+
try:
|
| 652 |
+
load_all_data()
|
| 653 |
+
print("β
Data reload completed successfully")
|
| 654 |
+
except Exception as reload_error:
|
| 655 |
+
print(f"β Reload error: {str(reload_error)[:100]}...")
|
| 656 |
+
else:
|
| 657 |
+
print("β³ Data already loading, skipping reload")
|
| 658 |
+
else:
|
| 659 |
+
# Only log status every 10 minutes to reduce spam
|
| 660 |
+
if datetime.now().minute % 10 == 0:
|
| 661 |
+
print(f"β
Watcher: No changes detected ({datetime.now().strftime('%H:%M')})")
|
| 662 |
+
else:
|
| 663 |
+
print("β οΈ Watcher: Could not extract last_updated info")
|
| 664 |
+
consecutive_errors += 1
|
| 665 |
+
|
| 666 |
+
# Reset error counter on successful operation
|
| 667 |
+
if details_info and consecutive_errors > 0:
|
| 668 |
+
print(f"β
Watcher: Connection restored (cleared {consecutive_errors} errors)")
|
| 669 |
+
consecutive_errors = 0
|
| 670 |
+
|
| 671 |
+
except Exception as e:
|
| 672 |
+
consecutive_errors += 1
|
| 673 |
+
print(f"β οΈ Watcher error #{consecutive_errors}: {str(e)[:100]}...")
|
| 674 |
+
|
| 675 |
+
# Force connection refresh after errors
|
| 676 |
+
watcher_client = None
|
| 677 |
+
watcher_spreadsheet = None
|
| 678 |
+
|
| 679 |
+
# Progressive backoff for multiple errors
|
| 680 |
+
if consecutive_errors >= max_errors:
|
| 681 |
+
error_wait = 300 # 5 minutes
|
| 682 |
+
print(f"β Too many watcher errors, waiting {error_wait//60} minutes...")
|
| 683 |
+
time.sleep(error_wait)
|
| 684 |
+
consecutive_errors = 0
|
| 685 |
+
|
| 686 |
+
# Wait 1 minute before next check
|
| 687 |
+
time.sleep(60)
|
| 688 |
+
|
| 689 |
def get_detailed_student_points(roll_no, studentwise_data):
|
| 690 |
"""Get detailed points breakdown from studentwise data"""
|
| 691 |
if not studentwise_data or len(studentwise_data) < 3:
|
|
|
|
| 827 |
refresh_thread.start()
|
| 828 |
print("π Auto-refresh thread started (updates every 12 hours)")
|
| 829 |
|
| 830 |
+
# 1 min auto-refresh thread to watch details sheet changes
|
| 831 |
+
watcher_thread = threading.Thread(target=details_sheet_watcher, daemon=True)
|
| 832 |
+
watcher_thread.start()
|
| 833 |
+
print("π Details sheet watcher started (checks every 1 minute)")
|
| 834 |
+
|
| 835 |
# Function to search student with cached data
|
| 836 |
def search_student(roll_no):
|
| 837 |
if not roll_no.strip():
|
|
|
|
| 1096 |
with gr.Blocks(title="Student Reward Points Check", theme=gr.themes.Soft()) as app:
|
| 1097 |
gr.Markdown("# π Student Reward Points Check")
|
| 1098 |
gr.Markdown("### Search for student details including reward points and redemption dates")
|
| 1099 |
+
gr.Markdown("π **Auto-Updates**: Data automatically refreshes when there is a change in Reward Points Sheet")
|
| 1100 |
+
gr.Markdown("### Fill this form for any issues/Feedback: [Issue/Feedback Form](https://docs.google.com/forms/d/e/1FAIpQLScnl0udcN2pUDENHl45HIj5HZbvDuwZ0g2eepBbp8tJYg-NvQ/viewform)")
|
| 1101 |
# Store admin components for conditional visibility
|
| 1102 |
admin_components = gr.State(None)
|
| 1103 |
|