Spaces:
Running
Running
| import os | |
| import time | |
| import requests | |
| import gradio as gr | |
| import openstack | |
| from openstack import exceptions | |
| # GLOBAL SHARED STATE | |
| active_user_count = 0 | |
| is_comfit_alive = False | |
| clean_link = "comfit-copilot.mch250095.projects.jetstream-cloud.org:3000" | |
| full_url = f"http://{clean_link}" | |
| # Styled HTML for a flashing neon green block with black text | |
| GREEN_FLASHING_LINK = f""" | |
| <style> | |
| @keyframes flash {{ | |
| 0% {{ opacity: 1; }} | |
| 50% {{ opacity: 0.4; }} | |
| 100% {{ opacity: 1; }} | |
| }} | |
| .flashing-btn {{ | |
| display: inline-block; | |
| padding: 20px 40px; | |
| font-size: 26px; | |
| font-weight: 900; | |
| text-decoration: none; | |
| color: black !important; | |
| background-color: #39FF14; | |
| border: 5px solid black; | |
| border-radius: 4px; | |
| text-align: center; | |
| animation: flash 1s infinite; | |
| box-shadow: 0 0 20px #39FF14; | |
| transition: transform 0.2s; | |
| }} | |
| .flashing-btn:hover {{ | |
| animation: none; | |
| transform: scale(1.05); | |
| opacity: 1; | |
| }} | |
| </style> | |
| <div style="text-align: center; margin: 30px 0;"> | |
| <a href="{full_url}" target="_blank" class="flashing-btn"> | |
| CLICK HERE FOR COMFIT COPILOT | |
| </a> | |
| </div> | |
| """ | |
| def get_conn(): | |
| """Authenticates with Jetstream2 (OpenStack) using Application Credentials.""" | |
| return openstack.connect( | |
| auth_url=os.getenv("OS_AUTH_URL"), | |
| application_credential_id=os.getenv("OS_APPLICATION_CREDENTIAL_ID"), | |
| application_credential_secret=os.getenv("OS_APPLICATION_CREDENTIAL_SECRET"), | |
| auth_type="v3applicationcredential", | |
| region_name="IU", | |
| interface="public" | |
| ) | |
| def get_current_status(): | |
| global active_user_count, is_comfit_alive | |
| try: | |
| conn = get_conn() | |
| instance_id = os.getenv("OS_INSTANCE_ID") | |
| server = conn.compute.find_server(instance_id) | |
| status_text = "β Error: Instance Not Found" | |
| if server: | |
| status_map = { | |
| "ACTIVE": "π’ RUNNING (ACTIVE)", | |
| "SHELVED_OFFLOADED": "π€ SLEEPING (SHELVED)", | |
| "SHUTOFF": "π΄ POWERED OFF", | |
| "BUILD": "π οΈ STARTING...", | |
| "SHELVING": "β³ SAVING STATE..." | |
| } | |
| status_text = status_map.get(server.status, f"β STATE: {server.status}") | |
| if "RUNNING" in status_text: | |
| try: | |
| if requests.get(full_url, timeout=2).status_code < 500: | |
| is_comfit_alive = True | |
| else: | |
| is_comfit_alive = False | |
| except: | |
| is_comfit_alive = False | |
| else: | |
| is_comfit_alive = False | |
| link_update = gr.update(value=GREEN_FLASHING_LINK if is_comfit_alive else "", visible=is_comfit_alive) | |
| return status_text, f"π₯ Active Users: {active_user_count}", link_update | |
| except Exception as e: | |
| return f"β Error: {str(e)}", f"π₯ Active Users: {active_user_count}", gr.update() | |
| def user_joined(): | |
| global active_user_count | |
| active_user_count += 1 | |
| return f"π₯ Active Users: {active_user_count}" | |
| def user_left(): | |
| global active_user_count | |
| active_user_count = max(0, active_user_count - 1) | |
| def manage_instance(): | |
| global is_comfit_alive | |
| try: | |
| conn = get_conn() | |
| instance_id = os.getenv("OS_INSTANCE_ID") | |
| server = conn.compute.find_server(instance_id) | |
| if not server: | |
| yield ("β Error: Instance Not Found", gr.update(visible=False)) | |
| return | |
| if server.status != "ACTIVE": | |
| yield ("β³ Milestone 1/3: Un-shelving Begin...", gr.update(visible=False)) | |
| if server.status == "SHELVED_OFFLOADED": | |
| conn.compute.unshelve_server(server) | |
| conn.compute.wait_for_server(server, status='ACTIVE', wait=600) | |
| yield ("β Milestone 2/3: Unshelved! VM is now ACTIVE.", gr.update(visible=False)) | |
| yield ("π Milestone 3/3: Connecting to ComFit...", gr.update(visible=False)) | |
| for i in range(24): | |
| try: | |
| if requests.get(full_url, timeout=3).status_code < 500: | |
| is_comfit_alive = True | |
| yield ("π Success! ComFit is ready.", gr.update(value=GREEN_FLASHING_LINK, visible=True)) | |
| return | |
| except: pass | |
| yield (f"π Milestone 3/3: Software booting ({i+1}/24)...", gr.update(visible=False)) | |
| time.sleep(5) | |
| except Exception as e: | |
| yield (f"β Error: {str(e)}", gr.update(visible=False)) | |
| combined_js = """ | |
| <script> | |
| let idleTime = 0; | |
| const MAX_IDLE = 10; | |
| function playNotification(status) { | |
| if (status.includes("RUNNING")) { | |
| var audio = new Audio('https://cdn.freesound.org'); | |
| audio.play().catch(e => console.log("Audio play blocked.")); | |
| } | |
| } | |
| function resetTimer() { idleTime = 0; } | |
| window.onmousemove = resetTimer; window.onmousedown = resetTimer; window.onclick = resetTimer; window.onkeypress = resetTimer; | |
| setInterval(function() { | |
| idleTime++; | |
| if (idleTime >= MAX_IDLE) { | |
| alert("Session Expired due to inactivity. Please refresh."); | |
| window.location.reload(); | |
| } | |
| }, 60000); | |
| </script> | |
| """ | |
| # FIX 1: Removed theme=gr.themes.Soft() from here | |
| with gr.Blocks() as demo: | |
| gr.HTML(combined_js) | |
| session_tracker = gr.State(value="active", delete_callback=user_left) | |
| gr.Markdown("# ComFit Copilot (Comfort & Fit Copilot) By Innovision") | |
| with gr.Row(): | |
| live_status = gr.Textbox(label="Live Jetstream2 Status (5s refresh)", interactive=False) | |
| user_display = gr.Textbox(label="Space Activity", interactive=False) | |
| with gr.Row(): | |
| activate_btn = gr.Button("π Activate / Access ComFit Copilot", variant="primary") | |
| refresh_btn = gr.Button("π Force Refresh Status", variant="secondary") | |
| status_label = gr.Label(value="Ready") | |
| # Flashing link box | |
| link_box = gr.HTML(visible=False) | |
| gr.Markdown("---") | |
| # CENTER ALIGNED INSTRUCTIONS HEADER | |
| gr.Markdown("<center><h3>π Instructions for Visitors</h3></center>") | |
| gr.Markdown(""" | |
| **Access or Wake Up** | |
| - If a flashing **Green Button** is already visible (above these instructions), simply click it to open ComFit Copilot. | |
| - If no flashing green button is visible, click **Activate / Access ComFit Copilot** button (top left). This will trigger the un-shelving process, which may take up to 2+ mins. | |
| **Note A: Track Milestones** | |
| The control panel will cycle through three milestones: Un-shelving, VM Activation, and Software Booting. Please wait while the cloud prepares your environment. | |
| **Note B: Automatic Session Timeout** | |
| If you do not interact with this page(or next page - ComFit Copilot interface) for **10 minutes**, your session will expire. | |
| **Note C: What to do after clicking on flashing green button** | |
| Once you sign-up using your email on ComFit Copilot page (next page), a button will pop-up showing that you need to confirm email. You can safely ignore this step(no need to open your inbox) and continue with login using information you just used for sign-up. Gmail/Microsoft 3rd party sign-up may not work for some users. | |
| """) | |
| # Logo Footer Section | |
| gr.Markdown("---") | |
| gr.Markdown("<center><h3>Thanks to our supporters & collaborators:</h3></center>") | |
| with gr.Row(): | |
| gr.Image("logo1.png", show_label=False, interactive=False, container=False, scale=1) | |
| gr.Image("logo2.png", show_label=False, interactive=False, container=False, scale=1) | |
| gr.Image("logo3.png", show_label=False, interactive=False, container=False, scale=1) | |
| live_status.change(None, inputs=live_status, js="(s) => playNotification(s)") | |
| activate_btn.click(fn=manage_instance, outputs=[status_label, link_box]) | |
| refresh_btn.click(fn=get_current_status, outputs=[live_status, user_display, link_box]) | |
| timer = gr.Timer(5) | |
| timer.tick(fn=get_current_status, outputs=[live_status, user_display, link_box]) | |
| demo.load(fn=user_joined, outputs=user_display) | |
| demo.load(fn=get_current_status, outputs=[live_status, user_display, link_box]) | |
| if __name__ == "__main__": | |
| # FIX 2: Added theme=gr.themes.Soft() here inside launch() | |
| demo.launch(theme=gr.themes.Soft()) | |