Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| def calculate_radiology_ai_roi(scans_per_day, radiologist_reading_time_minutes, er_time_to_treatment): | |
| scans_per_month = scans_per_day * 30 | |
| errors_reduced_per_month = int(scans_per_month * 0.05 * 0.20) | |
| hrs_saved_per_visit = round(er_time_to_treatment * 0.50 / 60, 2) | |
| time_saved_per_scan_minutes = radiologist_reading_time_minutes * 0.30 | |
| total_time_saved_hours = round((scans_per_month * time_saved_per_scan_minutes) / 60, 2) | |
| discrepant_cases_flagged = int(scans_per_month * 0.05) | |
| radiologist_cost_savings = f"${int(scans_per_month * 4):,}" | |
| return ( | |
| errors_reduced_per_month, | |
| discrepant_cases_flagged, | |
| hrs_saved_per_visit, | |
| total_time_saved_hours, | |
| radiologist_cost_savings | |
| ) | |
| def render_result_cards(e, f, h, t, c, active=False): | |
| # This function now generates the HTML for the result cards | |
| # The styling is handled by the embedded CSS | |
| return f""" | |
| <div class="result-grid"> | |
| <div class="result-card {"active" if active else "faded"}"> | |
| <div class="result-value">{e}</div> | |
| <div class="result-label">Errors reduced/month</div> | |
| </div> | |
| <div class="result-card {"active" if active else "faded"}"> | |
| <div class="result-value">{f}</div> | |
| <div class="result-label">Discrepant cases flagged/month</div> | |
| </div> | |
| <div class="result-card {"active" if active else "faded"}"> | |
| <div class="result-value">{h}</div> | |
| <div class="result-label">Hours saved per ER visit</div> | |
| </div> | |
| <div class="result-card {"active" if active else "faded"}"> | |
| <div class="result-value">{t}</div> | |
| <div class="result-label">Radiologist hours saved/month</div> | |
| </div> | |
| <div class="result-card {"active" if active else "faded"}"> | |
| <div class="result-value">{c}</div> | |
| <div class="result-label">Radiologist cost savings/month</div> | |
| </div> | |
| </div> | |
| """ | |
| def launch_ui(): | |
| # CSS is now embedded directly within the gr.Blocks call | |
| custom_css = """ | |
| /* General Body and Font Styles */ | |
| body { | |
| font-family: 'Inter', sans-serif; /* Using Inter font */ | |
| background-color: #000000; /* Completely black background */ | |
| color: #E0E0E0; /* Light gray for general text */ | |
| margin: 0; | |
| padding: 20px; | |
| display: flex; | |
| justify-content: center; | |
| align-items: flex-start; | |
| min-height: 100vh; | |
| box-sizing: border-box; | |
| } | |
| /* Main Gradio Container Styling */ | |
| .gradio-container { | |
| max-width: 1200px; | |
| width: 100%; | |
| background-color: #121212; /* Softer dark gray for the main container */ | |
| border-radius: 15px; | |
| box-shadow: 0 0 15px rgba(0, 0, 0, 0.5); /* Subtle dark shadow */ | |
| padding: 30px; | |
| box-sizing: border-box; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 25px; | |
| } | |
| /* Headings */ | |
| h1 { | |
| color: #FFFFFF; /* White for main heading */ | |
| text-align: center; | |
| text-shadow: none; /* No text glow for main heading */ | |
| margin-bottom: 5px; | |
| } | |
| h3 { | |
| color: #FFFFFF; /* White for subheadings */ | |
| text-align: center; | |
| text-shadow: none; /* No text glow for subheadings */ | |
| margin-bottom: 10px; | |
| } | |
| /* Markdown text (subheading below h1) */ | |
| .gradio-container > p { /* Target the direct child p for the subheading */ | |
| color: #FFFFFF; /* White for paragraph text */ | |
| text-align: center; | |
| margin-top: 5px; | |
| margin-bottom: 25px; | |
| } | |
| /* Main content row (inputs and results) */ | |
| .main-container { | |
| display: flex; | |
| flex-wrap: wrap; /* Allow wrapping on smaller screens */ | |
| gap: 30px; | |
| justify-content: center; | |
| align-items: flex-start; | |
| } | |
| /* Input Card Styling */ | |
| .input-card { | |
| flex: 1; | |
| min-width: 300px; /* Minimum width for input column */ | |
| background-color: #1E1E1E; /* Darker gray for input card */ | |
| padding: 25px; | |
| border-radius: 12px; | |
| box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); /* Softer shadow */ | |
| display: flex; | |
| flex-direction: column; | |
| gap: 15px; | |
| } | |
| /* Gradio Input Fields - Targeting for dark background and no borders/shadows */ | |
| .input-field label { | |
| color: #90EE90 !important; /* Lighter, more subtle green for input labels */ | |
| font-weight: bold; | |
| } | |
| /* Target the core input elements and their immediate containers */ | |
| .input-field .gr-block.gr-input, | |
| .input-field .gr-number input[type="number"], | |
| .input-field .gr-text-input, | |
| .input-field .gr-box, | |
| .input-field .gr-input-wrap { /* Added .gr-input-wrap for broader coverage */ | |
| background-color: #282828 !important; /* Dark background for inputs */ | |
| border: none !important; /* No border for inputs */ | |
| box-shadow: none !important; /* No shadow for inputs */ | |
| color: #E0E0E0 !important; /* Light text in input */ | |
| border-radius: 8px !important; | |
| padding: 10px 15px !important; | |
| font-size: 1em !important; | |
| transition: all 0.3s ease; | |
| } | |
| /* Ensure focus state is also clean */ | |
| .input-field .gr-number input[type="number"]:focus, | |
| .input-field .gr-text-input:focus { | |
| outline: none !important; | |
| border: 1px solid #00A000 !important; /* Very subtle muted green border on focus */ | |
| box-shadow: 0 0 5px rgba(0, 160, 0, 0.3) !important; /* Very soft glow on focus */ | |
| } | |
| /* Calculate Button Styling */ | |
| .calculate-button { | |
| background-color: #00A000 !important; /* Muted green button */ | |
| color: #FFFFFF !important; /* White text on button */ | |
| font-weight: bold !important; | |
| padding: 12px 25px !important; | |
| border-radius: 10px !important; | |
| border: none !important; | |
| cursor: pointer !important; | |
| font-size: 1.1em !important; | |
| transition: all 0.3s ease !important; | |
| box-shadow: 0 0 10px rgba(0, 160, 0, 0.4) !important; /* Subtle green glow */ | |
| } | |
| .calculate-button:hover { | |
| background-color: #00C000 !important; /* Slightly lighter green on hover */ | |
| box-shadow: 0 0 15px rgba(0, 192, 0, 0.6) !important; /* Stronger subtle glow on hover */ | |
| transform: translateY(-2px); | |
| } | |
| /* Results Section Styling */ | |
| .results-section { | |
| flex: 2; /* Take more space */ | |
| min-width: 400px; /* Minimum width for results column */ | |
| background-color: #1E1E1E; /* Darker gray for results card */ | |
| padding: 25px; | |
| border-radius: 12px; | |
| box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); /* Softer shadow */ | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| } | |
| /* Result Grid for Cards */ | |
| .result-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); /* Responsive grid for cards */ | |
| gap: 15px; | |
| margin-top: 15px; | |
| } | |
| /* Individual Result Card Styling - No borders, no shadows for a flat look */ | |
| .result-card { | |
| background-color: #282828; /* Darker background for cards */ | |
| border-radius: 10px; | |
| padding: 20px; | |
| text-align: center; | |
| box-shadow: none !important; /* No shadow */ | |
| border: none !important; /* No border */ | |
| transition: all 0.3s ease; | |
| } | |
| .result-card.active { | |
| background-color: #1a1a1a; /* Slightly darker when active */ | |
| /* No border or shadow here either for a flat look */ | |
| } | |
| .result-card.faded { | |
| opacity: 0.7; /* Slightly faded when not active */ | |
| } | |
| .result-value { | |
| font-size: 2.2em; | |
| font-weight: bold; | |
| color: #90EE90; /* Lighter, more subtle green for values */ | |
| margin-bottom: 5px; | |
| text-shadow: none; /* No text shadow for values */ | |
| } | |
| .result-label { | |
| font-size: 0.9em; | |
| color: #B0B0B0; /* Light gray for labels */ | |
| } | |
| /* Call to Action (CTA) Section */ | |
| .cta-section { | |
| background-color: #1E1E1E; /* Dark gray for CTA section */ | |
| padding: 25px; | |
| border-radius: 12px; | |
| text-align: center; | |
| margin-top: 30px; | |
| box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); /* Softer shadow */ | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 15px; | |
| } | |
| .cta-section p { | |
| font-size: 1.1em; | |
| color: #E0E0E0; | |
| margin-bottom: 0; /* Remove default margin */ | |
| } | |
| .cta-button { | |
| background-color: #00A000; /* Muted green button */ | |
| color: #FFFFFF; /* White text */ | |
| font-weight: bold; | |
| padding: 12px 30px; | |
| border-radius: 10px; | |
| text-decoration: none; /* No underline */ | |
| font-size: 1.1em; | |
| transition: all 0.3s ease; | |
| box-shadow: 0 0 10px rgba(0, 160, 0, 0.4); /* Subtle green glow */ | |
| display: inline-block; /* Allows padding and margin */ | |
| } | |
| .cta-button:hover { | |
| background-color: #00C000; /* Slightly lighter muted green on hover */ | |
| box-shadow: 0 0 15px rgba(0, 192, 0, 0.6); /* Stronger subtle glow */ | |
| transform: translateY(-2px); | |
| } | |
| /* Responsive adjustments */ | |
| @media (max-width: 768px) { | |
| .main-container { | |
| flex-direction: column; /* Stack columns on small screens */ | |
| align-items: center; | |
| } | |
| .input-card, .results-section { | |
| width: 100%; /* Full width on small screens */ | |
| min-width: unset; /* Remove min-width constraint */ | |
| } | |
| .result-grid { | |
| grid-template-columns: 1fr; /* Single column for result cards on very small screens */ | |
| } | |
| .gradio-container { | |
| padding: 15px; | |
| } | |
| } | |
| """ | |
| with gr.Blocks(css=custom_css, title="Radiology ROI Calculator") as demo: | |
| gr.Markdown("# Estimate the Value of AI in Your Practice") | |
| gr.Markdown("Calculate the ROI of MSK AI in emergency radiology settings.") | |
| with gr.Row(elem_classes="main-container"): # Use a new class for the main layout | |
| with gr.Column(elem_classes="input-card"): # Class for the input section | |
| gr.Markdown("### Your Inputs") | |
| scans = gr.Number(label="Scans per day", value=100, elem_classes="input-field") | |
| reading_time = gr.Number(label="Radiologist time per scan (minutes)", value=3, elem_classes="input-field") | |
| er_time = gr.Number(label="ER time to treatment (minutes)", value=60, elem_classes="input-field") | |
| submit = gr.Button("Calculate ROI", elem_classes="calculate-button") | |
| with gr.Column(elem_classes="results-section"): # Class for the results section | |
| gr.Markdown("### Results") | |
| result_container = gr.HTML(render_result_cards('--', '--', '--', '--', '--')) | |
| # The CTA section, now with custom styling | |
| gr.HTML(""" | |
| <div class="cta-section"> | |
| <p>Want to see how this works in action?</p> | |
| <a href="https://carpl.ai/contact-us" target="_blank" class="cta-button">Book a Demo</a> | |
| </div> | |
| """) | |
| submit.click( | |
| fn=lambda s, r, e: render_result_cards(*calculate_radiology_ai_roi(s, r, e), active=True), | |
| inputs=[scans, reading_time, er_time], | |
| outputs=[result_container] | |
| ) | |
| demo.launch() | |
| if __name__ == "__main__": | |
| launch_ui() | |