MediDashboard / app.py
TKM03's picture
Update app.py
bad7662 verified
import gradio as gr
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from selenium.webdriver.edge.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait, Select
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from datetime import datetime
import time
import numpy as np
from selenium.common.exceptions import StaleElementReferenceException, TimeoutException, NoSuchElementException
#--- Scraping Functions (Unchanged) ---
def wait_for_element(driver, locator):
return WebDriverWait(driver, 10).until(EC.element_to_be_clickable(locator))
def select_date_month_day(driver, date_str, date_input_id):
date_to_select = datetime.strptime(date_str, '%Y-%m-%d')
date_input = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, date_input_id)))
date_input.click()
month_select = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, 'ui-datepicker-month')))
month_option = month_select.find_element(By.XPATH, f"//option[@value='{date_to_select.month - 1}']")
month_option.click()
day = date_to_select.day
day_element = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, f"//td[@data-handler='selectDay']/a[text()='{day}']")))
day_element.click()
time.sleep(5)
def select_date(driver, date_str, date_input_id):
date_to_select = datetime.strptime(date_str, '%Y-%m-%d')
date_input = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, date_input_id)))
date_input.click()
time.sleep(2)
month_select = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, 'ui-datepicker-month')))
month_option = month_select.find_element(By.XPATH, f"//option[@value='{date_to_select.month - 1}']")
time.sleep(2)
month_option.click()
year_select = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, 'ui-datepicker-year')))
year_option = year_select.find_element(By.XPATH, f"//option[@value='{date_to_select.year}']")
year_option.click()
day = date_to_select.day
day_element = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, f"//td[@data-handler='selectDay']/a[text()='{day}']")))
day_element.click()
time.sleep(5)
def extract_grid_data_clm_summary(driver):
data = []
try:
total_pages_element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "sp_1_pjqgridClmSummbyProv")))
total_pages = int(total_pages_element.text.strip())
except:
return data
for current_page in range(1, total_pages + 1):
time.sleep(3)
driver.execute_script("window.scrollTo(0, 0);")
grid = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, "jqgridClmSummbyProv")))
rows = WebDriverWait(grid, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "tr.jqgrow")))
for row in rows:
try:
provider_name = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgridClmSummbyProv_STR_ProvName']").text
visits = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgridClmSummbyProv_STR_NoOfVisit']").text
claim = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgridClmSummbyProv_STR_ClmAmt']").text
try:
total_mc = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgridClmSummbyProv_STR_TotalMC']").text
except:
total_mc = '0'
data.append({'Provider Name': provider_name, 'No of Visits': visits, 'Total Claim': claim, 'Total MC (Days)': total_mc})
except Exception as e:
print(f"Error extracting row: {e}")
continue
if current_page < total_pages:
try:
next_button_div = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div.btn.btn-sm.btn-default span.fa.fa-forward")))
driver.execute_script("arguments[0].scrollIntoView(true);", next_button_div)
next_button_div.click()
WebDriverWait(driver, 10).until(EC.staleness_of(rows[0]))
except:
break
return data
def extract_grid_data_patient_analysis(driver):
all_data = []
while True:
driver.execute_script("window.scrollTo(0, 0);")
grid = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "jqgridCorpMcAnalysis")))
rows = grid.find_elements(By.CSS_SELECTOR, "tr.jqgrow")
for row in rows:
try:
employee_name = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgridCorpMcAnalysis_MEM_NAME']").text
employee_no = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgridCorpMcAnalysis_MEM_EMPID']").text
division = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgridCorpMcAnalysis_MEM_EMPDIVISION']").text
total_visit = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgridCorpMcAnalysis_totalVisit']").text
total_mc = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgridCorpMcAnalysis_totalMC']").text
total_claim_own = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgridCorpMcAnalysis_totalClaim_Own']").text
total_claim_dep = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgridCorpMcAnalysis_totalClaim_Dep']").text
all_data.append({
'Employee Name': employee_name, 'Employee No': employee_no, 'Division/Department': division,
'Total Visit': total_visit, 'Total MC (Days)': total_mc, 'Total Claim (Own)': total_claim_own,
'Total Claim (Dep)': total_claim_dep
})
except:
continue
try:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
next_button = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div.btn.btn-sm.btn-default span.fa.fa-forward")))
parent_div = next_button.find_element(By.XPATH, "./parent::div")
if "disabled" in parent_div.get_attribute("class"):
break
driver.execute_script("arguments[0].scrollIntoView(true);", parent_div)
parent_div.click()
WebDriverWait(driver, 10).until(EC.staleness_of(rows[0]))
except:
break
return all_data
def extract_grid_data_mc(driver):
data = []
try:
total_pages_element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "sp_1_jqgrid")))
total_pages = int(total_pages_element.text.strip())
except:
total_pages = 1
for current_page in range(1, total_pages + 1):
time.sleep(3)
driver.execute_script("window.scrollTo(0, 0);")
grid = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, "jqgrid")))
rows = WebDriverWait(grid, 10).until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, "tr.jqgrow")))
for row in rows:
try:
provider_name = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgrid_STR_ProvName']").text.strip()
total_mc_given = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgrid_STR_MC_Given_Count']").text.strip()
total_visits = row.find_element(By.CSS_SELECTOR, "td[aria-describedby='jqgrid_STR_VISITCount']").text.strip()
data.append({'Provider': provider_name, 'Total MC Given': total_mc_given, 'No. of Visit': total_visits})
except:
continue
if current_page < total_pages:
try:
next_button_div = WebDriverWait(driver, 5).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "div.btn.btn-sm.btn-default span.fa.fa-forward")))
driver.execute_script("arguments[0].scrollIntoView(true);", next_button_div)
next_button_div.click()
WebDriverWait(driver, 10).until(EC.staleness_of(rows[0]))
except:
break
return data
def scrape_data(url, user_id, password):
edge_options = Options()
driver = webdriver.Edge(service=Service(EdgeChromiumDriverManager().install()), options=edge_options)
start_year = 2024
current_date = datetime.now().strftime('%Y-%m-%d')
patient_data_by_year = {}
claim_data_by_year = {}
mc_data_by_year = {}
try:
driver.get(url)
image = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//img[@src='/ClaimEXMVR/Servlet_LoadImage?SFC=loadImage&imageName=icorporate.png']")))
image.click()
user_id_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.NAME, "txtloginid")))
user_id_field.send_keys(user_id)
password_field = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.ID, "inputpss")))
password_field.send_keys(password)
sign_button = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.btn.btn-primary[type='submit']")))
sign_button.click()
continue_button = wait_for_element(driver, (By.XPATH, "//button[text()='Continue']"))
continue_button.click()
for year in range(start_year, 2025 + 1):
if year == 2024:
start_date = "2024-01-01"
end_date = "2024-12-31"
else:
start_date = "2025-01-01"
end_date = current_date
productivity_link = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//a[span[text()='Productivity Reports']]")))
productivity_link.click()
patient_analysis_link = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//a[@href='#/Patient_Analysis_Report'][span[text()=' Patient Analysis Report ']]")))
patient_analysis_link.click()
select_date(driver, start_date, "txtStartDate")
select_date(driver, end_date, "txtEndDate")
search_button = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "btnSearch")))
search_button.click()
time.sleep(5)
dropdown = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "select.ui-pg-selbox")))
driver.execute_script("arguments[0].scrollIntoView(true);", dropdown)
select = Select(dropdown)
select.select_by_value("100")
time.sleep(10)
patient_data = extract_grid_data_patient_analysis(driver)
patient_df = pd.DataFrame(patient_data)
numeric_cols_patient = ['Total Visit', 'Total MC (Days)', 'Total Claim (Own)', 'Total Claim (Dep)']
for col in numeric_cols_patient:
patient_df[col] = pd.to_numeric(patient_df[col], errors='coerce')
patient_df['Total Claim (Combined)'] = patient_df['Total Claim (Own)'] + patient_df['Total Claim (Dep)']
patient_df['Avg Claim per Visit'] = patient_df['Total Claim (Combined)'] / patient_df['Total Visit']
patient_df['Avg MC per Visit'] = patient_df['Total MC (Days)'] / patient_df['Total Visit']
patient_df['Avg Claim per MC'] = patient_df['Total Claim (Combined)'] / patient_df['Total MC (Days)']
patient_data_by_year[year] = patient_df
for year in range(start_year, 2025 + 1):
if year == 2024:
start_date = "2024-01-01"
end_date = "2024-12-31"
else:
start_date = "2025-01-01"
end_date = current_date
productivity_link = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//a[span[text()='Productivity Reports']]")))
productivity_link.click()
mc_link = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//a[@href='#/MC_HealthCare_By_Provider'][span[text()=' MC by Provider ']]")))
mc_link.click()
time.sleep(2)
select_date(driver, start_date, "txtStartDate")
select_date(driver, end_date, "txtEndDate")
search_button = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "btnSearch")))
search_button.click()
time.sleep(10)
dropdown = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "select.ui-pg-selbox")))
driver.execute_script("arguments[0].scrollIntoView(true);", dropdown)
select = Select(dropdown)
select.select_by_value("100")
time.sleep(10)
mc_data = extract_grid_data_mc(driver)
mc_df = pd.DataFrame(mc_data)
numeric_cols_mc = ['Total MC Given', 'No. of Visit']
for col in numeric_cols_mc:
mc_df[col] = pd.to_numeric(mc_df[col], errors='coerce')
mc_df['% MC Given'] = (mc_df['Total MC Given'] / mc_df['No. of Visit']) * 100
mc_data_by_year[year] = mc_df
start_date = "2024-01-01"
end_date = "2025-01-01"
reg_claims_link = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//a[.//span[contains(text(), 'Registration') and contains(text(), 'Claims')]]")))
reg_claims_link.click()
providers_link = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//a[@href='#/Claim_Summary_by_Provider_Analysis'][span[text()=' Claim Summary by Providers ']]")))
providers_link.click()
time.sleep(5)
select_date_month_day(driver, start_date, "txtFromDate")
select_date_month_day(driver, end_date, "txtToDate")
search_button = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "btnSearch")))
driver.execute_script("arguments[0].click();", search_button)
time.sleep(10)
dropdown = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "select.ui-pg-selbox")))
driver.execute_script("arguments[0].scrollIntoView(true);", dropdown)
select = Select(dropdown)
select.select_by_value("100")
time.sleep(5)
claim_data_2024 = extract_grid_data_clm_summary(driver)
claim_df_2024 = pd.DataFrame(claim_data_2024)
numeric_cols_claim = ['No of Visits', 'Total Claim', 'Total MC (Days)']
for col in numeric_cols_claim:
claim_df_2024[col] = pd.to_numeric(claim_df_2024[col], errors='coerce')
claim_df_2024['Avg Claim per Visit'] = claim_df_2024['Total Claim'] / claim_df_2024['No of Visits']
claim_data_by_year[2024] = claim_df_2024
start_date = "2024-12-31"
end_date = current_date
reg_claims_link = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//a[.//span[contains(text(), 'Registration') and contains(text(), 'Claims')]]")))
reg_claims_link.click()
providers_link = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//a[@href='#/Claim_Summary_by_Provider_Analysis'][span[text()=' Claim Summary by Providers ']]")))
providers_link.click()
time.sleep(5)
select_date_month_day(driver, start_date, "txtFromDate")
select_date_month_day(driver, end_date, "txtToDate")
search_button = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.ID, "btnSearch")))
driver.execute_script("arguments[0].click();", search_button)
time.sleep(10)
dropdown = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "select.ui-pg-selbox")))
driver.execute_script("arguments[0].scrollIntoView(true);", dropdown)
select = Select(dropdown)
select.select_by_value("100")
time.sleep(5)
claim_data_recent = extract_grid_data_clm_summary(driver)
claim_df_recent = pd.DataFrame(claim_data_recent)
for col in numeric_cols_claim:
claim_df_recent[col] = pd.to_numeric(claim_df_recent[col], errors='coerce')
claim_df_recent['Avg Claim per Visit'] = claim_df_recent['Total Claim'] / claim_df_recent['No of Visits']
claim_data_by_year[2025] = claim_df_recent
return patient_data_by_year, claim_data_by_year, mc_data_by_year, "Data scraped successfully!"
except Exception as e:
return None, None, None, f"Error: {str(e)}"
finally:
driver.quit()
# --- Plotting Functions ---
def generate_dashboard_charts(patient_data_by_year, claim_data_by_year, mc_data_by_year, year, show_mc_pct=True, mc_sort_order="desc"):
if not patient_data_by_year or not claim_data_by_year or not mc_data_by_year:
return None, None
year_int = int(year)
patient_df = patient_data_by_year.get(year_int, pd.DataFrame())
claim_df = claim_data_by_year.get(year_int, pd.DataFrame())
mc_df = mc_data_by_year.get(year_int, pd.DataFrame())
if patient_df.empty or claim_df.empty or mc_df.empty:
return None, None
# Professional styling
sns.set(style="whitegrid", palette="muted")
plt.rcParams.update({
'font.family': 'Helvetica', 'font.size': 12, 'axes.titlesize': 16,
'axes.labelsize': 14, 'xtick.labelsize': 11, 'ytick.labelsize': 11,
'axes.titleweight': 'bold', 'axes.linewidth': 1.5, 'grid.linestyle': ':',
'grid.alpha': 0.5, 'figure.facecolor': '#f5f6f5', 'axes.facecolor': '#ffffff',
'axes.edgecolor': '#333333', 'axes.labelcolor': '#333333', 'text.color': '#333333'
})
provider_charts = []
employee_charts = []
# --- Provider Dashboard Charts ---
# 1. Total Visits by Providers (Horizontal Bar)
plt.figure(figsize=(12, 6))
top_prov_visits = mc_df.sort_values('No. of Visit', ascending=False).head(10)
ax = sns.barplot(data=top_prov_visits, x='No. of Visit', y='Provider', hue='Provider', palette='Blues_r', legend=False, edgecolor='black', linewidth=0.5)
ax.set_title(f'Top 10 Providers by Total Visits ({year})', pad=15)
ax.set_xlabel('Total Visits')
ax.set_ylabel('Provider')
for i, v in enumerate(top_prov_visits['No. of Visit']):
ax.text(v + 0.5, i, f'{int(v)}', va='center', fontsize=10, color='#333333')
plt.tight_layout()
provider_charts.append(plt.gcf())
plt.close()
# 2. Total MC by Providers (Horizontal Bar)
plt.figure(figsize=(12, 6))
top_prov_mc = mc_df.sort_values('Total MC Given', ascending=False).head(10)
ax = sns.barplot(data=top_prov_mc, x='Total MC Given', y='Provider', hue='Provider', palette='Greens_r', legend=False, edgecolor='black', linewidth=0.5)
ax.set_title(f'Top 10 Providers by Total MC Given ({year})', pad=15)
ax.set_xlabel('Total MC (Days)')
ax.set_ylabel('Provider')
for i, v in enumerate(top_prov_mc['Total MC Given']):
ax.text(v + 0.5, i, f'{int(v)}', va='center', fontsize=10, color='#333333')
plt.tight_layout()
provider_charts.append(plt.gcf())
plt.close()
# 3. % MC Given by Providers (Larger Bar, Top 20, Sortable)
if show_mc_pct:
plt.figure(figsize=(18, 9)) # Larger size for professionalism and readability
top_visits_provs = set(mc_df.sort_values('No. of Visit', ascending=False).head(10)['Provider'])
top_mc_provs = set(mc_df.sort_values('Total MC Given', ascending=False).head(10)['Provider'])
top_provs = top_visits_provs.union(top_mc_provs)
top_prov_mc_pct = mc_df[mc_df['Provider'].isin(top_provs)].sort_values(
'% MC Given', ascending=(mc_sort_order == "asc")).head(20)
ax = sns.barplot(data=top_prov_mc_pct, x='Provider', y='% MC Given', hue='Provider',
palette='Purples_r', legend=False, edgecolor='black', linewidth=0.5)
ax.set_title(f'Top 20 Providers by % MC Given ({year}) - Sorted {"Ascending" if mc_sort_order == "asc" else "Descending"}', pad=15)
ax.set_ylabel('% MC Given', fontsize=14)
ax.set_xlabel('Provider', fontsize=14)
plt.xticks(rotation=45, ha='right', fontsize=11)
for i, v in enumerate(top_prov_mc_pct['% MC Given']):
ax.text(i, v + 1, f'{v:.1f}%', ha='center', fontsize=10, color='#333333')
plt.tight_layout()
provider_charts.append(plt.gcf())
else:
provider_charts.append(None)
plt.close()
# 4. Total Claim by Providers (Horizontal Bar)
plt.figure(figsize=(12, 6))
top_prov_claim = claim_df.sort_values('Total Claim', ascending=False).head(10)
ax = sns.barplot(data=top_prov_claim, x='Total Claim', y='Provider Name', hue='Provider Name',
palette='Oranges_r', legend=False, edgecolor='black', linewidth=0.5)
ax.set_title(f'Top 10 Providers by Total Claim ({year})', pad=15)
ax.set_xlabel('Total Claim ($)')
ax.set_ylabel('Provider')
for i, v in enumerate(top_prov_claim['Total Claim']):
ax.text(v + 0.5, i, f'{v:,.2f}', va='center', fontsize=10, color='#333333')
plt.tight_layout()
provider_charts.append(plt.gcf())
plt.close()
# 5. Average Claim per Visit by Providers (Bar)
plt.figure(figsize=(12, 6))
top_prov_avg_claim = claim_df.sort_values('Avg Claim per Visit', ascending=False).head(10)
ax = sns.barplot(data=top_prov_avg_claim, x='Provider Name', y='Avg Claim per Visit', hue='Provider Name',
palette='Reds_r', legend=False, edgecolor='black', linewidth=0.5)
ax.set_title(f'Top 10 Providers by Avg Claim per Visit ({year})', pad=15)
ax.set_ylabel('Avg Claim per Visit ($)')
ax.set_xlabel('Provider')
plt.xticks(rotation=45, ha='right')
for i, v in enumerate(top_prov_avg_claim['Avg Claim per Visit']):
ax.text(i, v + 0.5, f'{v:.2f}', ha='center', fontsize=10, color='#333333')
plt.tight_layout()
provider_charts.append(plt.gcf())
plt.close()
# --- Employee Dashboard Charts ---
# 1. Total Visits by Employees
plt.figure(figsize=(12, 6))
top_emp_visits = patient_df.sort_values('Total Visit', ascending=False).head(10)
ax = sns.barplot(data=top_emp_visits, x='Total Visit', y='Employee Name', hue='Employee Name',
palette='Blues_r', legend=False, edgecolor='black', linewidth=0.5)
ax.set_title(f'Top 10 Employees by Total Visits ({year})', pad=15)
ax.set_xlabel('Total Visits')
ax.set_ylabel('Employee')
for i, v in enumerate(top_emp_visits['Total Visit']):
ax.text(v + 0.2, i, f'{int(v)}', va='center', fontsize=10, color='#333333')
plt.tight_layout()
employee_charts.append(plt.gcf())
plt.close()
# 2. Total Claim by Employees
plt.figure(figsize=(12, 6))
top_emp_claim = patient_df.sort_values('Total Claim (Combined)', ascending=False).head(10)
ax = sns.barplot(data=top_emp_claim, x='Total Claim (Combined)', y='Employee Name', hue='Employee Name',
palette='Oranges_r', legend=False, edgecolor='black', linewidth=0.5)
ax.set_title(f'Top 10 Employees by Total Claim ({year})', pad=15)
ax.set_xlabel('Total Claim ($)')
ax.set_ylabel('Employee')
for i, v in enumerate(top_emp_claim['Total Claim (Combined)']):
ax.text(v + 1, i, f'{v:,.2f}', va='center', fontsize=10, color='#333333')
plt.tight_layout()
employee_charts.append(plt.gcf())
plt.close()
# 3. Average Claim per Visit by Employees
plt.figure(figsize=(12, 6))
top_emp_avg_claim = patient_df.sort_values('Avg Claim per Visit', ascending=False).head(10)
ax = sns.barplot(data=top_emp_avg_claim, x='Employee Name', y='Avg Claim per Visit', hue='Employee Name',
palette='Reds_r', legend=False, edgecolor='black', linewidth=0.5)
ax.set_title(f'Top 10 Employees by Avg Claim per Visit ({year})', pad=15)
ax.set_ylabel('Avg Claim per Visit ($)')
ax.set_xlabel('Employee')
plt.xticks(rotation=45, ha='right')
for i, v in enumerate(top_emp_avg_claim['Avg Claim per Visit']):
ax.text(i, v + 0.5, f'{v:.2f}', ha='center', fontsize=10, color='#333333')
plt.tight_layout()
employee_charts.append(plt.gcf())
plt.close()
# 4. Total MC by Employees
plt.figure(figsize=(12, 6))
top_emp_mc = patient_df.sort_values('Total MC (Days)', ascending=False).head(10)
ax = sns.barplot(data=top_emp_mc, x='Total MC (Days)', y='Employee Name', hue='Employee Name',
palette='Greens_r', legend=False, edgecolor='black', linewidth=0.5)
ax.set_title(f'Top 10 Employees by Total MC ({year})', pad=15)
ax.set_xlabel('Total MC (Days)')
ax.set_ylabel('Employee')
for i, v in enumerate(top_emp_mc['Total MC (Days)']):
ax.text(v + 0.2, i, f'{int(v)}', va='center', fontsize=10, color='#333333')
plt.tight_layout()
employee_charts.append(plt.gcf())
plt.close()
# 5. Average MC per Visit by Employees
plt.figure(figsize=(12, 6))
top_emp_avg_mc = patient_df.sort_values('Avg MC per Visit', ascending=False).head(10)
ax = sns.barplot(data=top_emp_avg_mc, x='Employee Name', y='Avg MC per Visit', hue='Employee Name',
palette='Purples_r', legend=False, edgecolor='black', linewidth=0.5)
ax.set_title(f'Top 10 Employees by Avg MC per Visit ({year})', pad=15)
ax.set_ylabel('Avg MC per Visit (Days)')
ax.set_xlabel('Employee')
plt.xticks(rotation=45, ha='right')
for i, v in enumerate(top_emp_avg_mc['Avg MC per Visit']):
ax.text(i, v + 0.05, f'{v:.2f}', ha='center', fontsize=10, color='#333333')
plt.tight_layout()
employee_charts.append(plt.gcf())
plt.close()
# 6. Division-wise Claim Distribution (Pie)
plt.figure(figsize=(10, 6))
division_claims = patient_df.groupby('Division/Department')['Total Claim (Combined)'].sum()
plt.pie(division_claims, labels=division_claims.index, autopct='%1.1f%%', colors=sns.color_palette('muted'),
startangle=90, textprops={'fontsize': 11, 'color': '#333333'}, wedgeprops={'edgecolor': 'black', 'linewidth': 0.5})
plt.title(f'Claim Distribution by Division ({year})', pad=15)
plt.tight_layout()
employee_charts.append(plt.gcf())
plt.close()
provider_images = [fig_to_image(fig) if fig is not None else None for fig in provider_charts]
employee_images = [fig_to_image(fig) for fig in employee_charts]
return provider_images, employee_images
def fig_to_image(fig):
if fig is None:
return None
fig.canvas.draw()
img_array = np.frombuffer(fig.canvas.buffer_rgba(), dtype=np.uint8)
img_array = img_array.reshape(fig.canvas.get_width_height()[::-1] + (4,))
plt.close(fig)
return img_array
# --- Gradio Interface ---
with gr.Blocks(title="Claims Analysis Dashboard", css="""
body { background-color: #f5f6f5; }
h1, h2 { color: #333333; font-family: Helvetica; }
""") as demo:
gr.Markdown("# Claims Analysis Dashboard (2024 - Present)")
with gr.Row():
url_input = gr.Textbox(label="Website URL", placeholder="Enter URL here", lines=1)
user_id_input = gr.Textbox(label="User ID", placeholder="Enter User ID", lines=1)
password_input = gr.Textbox(label="Password", type="password", placeholder="Enter Password", lines=1)
scrape_btn = gr.Button("Submit", variant="primary")
with gr.Row():
year_dropdown = gr.Dropdown(
label="Select Year to View Data",
choices=["2024", "2025"],
value="2024",
allow_custom_value=False
)
show_mc_pct_checkbox = gr.Checkbox(label="Show % MC Given Chart", value=True)
mc_sort_dropdown = gr.Dropdown(
label="Sort % MC Given",
choices=["desc", "asc"],
value="desc",
allow_custom_value=False
)
status_output = gr.Textbox(label="Status", lines=2, interactive=False)
patient_state = gr.State()
claim_state = gr.State()
mc_state = gr.State()
with gr.Tabs():
with gr.TabItem("Provider Insights"):
gr.Markdown("## Provider Insights Dashboard")
with gr.Row():
prov_chart1 = gr.Image(label="Total Visits by Providers", interactive=False)
prov_chart2 = gr.Image(label="Total MC by Providers", interactive=False)
with gr.Row():
prov_chart3 = gr.Image(label="% MC Given by Providers (Top 20)", interactive=False, visible=True)
with gr.Row():
prov_chart4 = gr.Image(label="Total Claim by Providers", interactive=False)
prov_chart5 = gr.Image(label="Average Claim per Visit by Providers", interactive=False)
with gr.TabItem("Employee Insights"):
gr.Markdown("## Employee Insights Dashboard")
with gr.Row():
emp_chart1 = gr.Image(label="Total Visits by Employees", interactive=False)
emp_chart2 = gr.Image(label="Total Claim by Employees", interactive=False)
with gr.Row():
emp_chart3 = gr.Image(label="Average Claim per Visit by Employees", interactive=False)
emp_chart4 = gr.Image(label="Total MC by Employees", interactive=False)
with gr.Row():
emp_chart5 = gr.Image(label="Average MC per Visit by Employees", interactive=False)
emp_chart6 = gr.Image(label="Claim Distribution by Division", interactive=False)
# [Event handlers remain unchanged, just ensure inputs/outputs match the above components]
def scrape_and_store(url, user_id, password, show_mc_pct, mc_sort_order):
patient_data_by_year, claim_data_by_year, mc_data_by_year, status = scrape_data(url, user_id, password)
if patient_data_by_year is None or claim_data_by_year is None or mc_data_by_year is None:
return status, None, None, None
provider_images, employee_images = generate_dashboard_charts(
patient_data_by_year, claim_data_by_year, mc_data_by_year, "2024", show_mc_pct, mc_sort_order)
return (
status, patient_data_by_year, claim_data_by_year, mc_data_by_year,
provider_images[0], provider_images[1], provider_images[2], provider_images[3], provider_images[4],
employee_images[0], employee_images[1], employee_images[2], employee_images[3], employee_images[4], employee_images[5]
)
def update_dashboard(year, patient_data_by_year, claim_data_by_year, mc_data_by_year, show_mc_pct, mc_sort_order):
if not patient_data_by_year or not claim_data_by_year or not mc_data_by_year:
return [None] * 11
provider_images, employee_images = generate_dashboard_charts(
patient_data_by_year, claim_data_by_year, mc_data_by_year, year, show_mc_pct, mc_sort_order)
return (
provider_images[0], provider_images[1], provider_images[2], provider_images[3], provider_images[4],
employee_images[0], employee_images[1], employee_images[2], employee_images[3], employee_images[4], employee_images[5]
)
scrape_btn.click(
fn=lambda url, user_id, password: scrape_and_store(url, user_id, password, show_mc_pct_checkbox.value, mc_sort_dropdown.value),
inputs=[url_input, user_id_input, password_input],
outputs=[
status_output, patient_state, claim_state, mc_state,
prov_chart1, prov_chart2, prov_chart3, prov_chart4, prov_chart5,
emp_chart1, emp_chart2, emp_chart3, emp_chart4, emp_chart5, emp_chart6
]
)
year_dropdown.change(
fn=update_dashboard,
inputs=[year_dropdown, patient_state, claim_state, mc_state, show_mc_pct_checkbox, mc_sort_dropdown],
outputs=[
prov_chart1, prov_chart2, prov_chart3, prov_chart4, prov_chart5,
emp_chart1, emp_chart2, emp_chart3, emp_chart4, emp_chart5, emp_chart6
]
)
show_mc_pct_checkbox.change(
fn=update_dashboard,
inputs=[year_dropdown, patient_state, claim_state, mc_state, show_mc_pct_checkbox, mc_sort_dropdown],
outputs=[
prov_chart1, prov_chart2, prov_chart3, prov_chart4, prov_chart5,
emp_chart1, emp_chart2, emp_chart3, emp_chart4, emp_chart5, emp_chart6
]
)
mc_sort_dropdown.change(
fn=update_dashboard,
inputs=[year_dropdown, patient_state, claim_state, mc_state, show_mc_pct_checkbox, mc_sort_dropdown],
outputs=[
prov_chart1, prov_chart2, prov_chart3, prov_chart4, prov_chart5,
emp_chart1, emp_chart2, emp_chart3, emp_chart4, emp_chart5, emp_chart6
]
)
demo.launch(share=True)