Alex Amari commited on
Commit
389f234
·
1 Parent(s): ea2e95c

Restyle app to match OHA branding

Browse files
Files changed (3) hide show
  1. __pycache__/app.cpython-312.pyc +0 -0
  2. app.py +82 -19
  3. requirements.txt +1 -0
__pycache__/app.cpython-312.pyc CHANGED
Binary files a/__pycache__/app.cpython-312.pyc and b/__pycache__/app.cpython-312.pyc differ
 
app.py CHANGED
@@ -16,6 +16,7 @@ import gradio as gr
16
  from openai import OpenAI
17
  from geopy.geocoders import Nominatim
18
  from geopy.distance import geodesic
 
19
  import time
20
 
21
  OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
@@ -150,6 +151,7 @@ TRANSLATIONS = {
150
  "continue_button": "Continue",
151
  "back_button": "Back",
152
  "miles_away": "miles away",
 
153
  "free_care": "Free care up to",
154
  "sliding_scale": "Sliding scale up to",
155
  "status": "Status",
@@ -179,6 +181,7 @@ TRANSLATIONS = {
179
  "continue_button": "Continuar",
180
  "back_button": "Atrás",
181
  "miles_away": "millas de distancia",
 
182
  "free_care": "Atención gratuita hasta",
183
  "sliding_scale": "Escala móvil hasta",
184
  "status": "Estado",
@@ -216,8 +219,8 @@ def _geocode_zip(zip_code):
216
  _zip_geocode_cache[zip_code] = location
217
  return location
218
 
219
- def find_nearby_hospitals(zip_code, lang):
220
- """Find the 3 nearest CT hospitals to a given ZIP code.
221
 
222
  Validates the ZIP is a CT format (060xx-069xx), geocodes it,
223
  then returns markdown cards and choice labels sorted by distance.
@@ -239,7 +242,7 @@ def find_nearby_hospitals(zip_code, lang):
239
  distances.append((name, dist, data))
240
 
241
  distances.sort(key=lambda x: x[1])
242
- nearest = distances[:3]
243
 
244
  choices = []
245
  cards = ""
@@ -1032,8 +1035,8 @@ def create_interface():
1032
  gr.HTML(
1033
  '<div class="oha-header">'
1034
  '<a href="https://portal.ct.gov/oha" target="_blank" rel="noopener">'
1035
- '<h1>Office of the Healthcare Advocate<span class="oha-beta">BETA</span></h1>'
1036
- '<div class="oha-subtitle">Hospital Financial Assistance Eligibility Screener</div>'
1037
  '</a>'
1038
  '</div>'
1039
  )
@@ -1049,6 +1052,15 @@ def create_interface():
1049
  container=False
1050
  )
1051
 
 
 
 
 
 
 
 
 
 
1052
  # Step 1: Choose path
1053
  step1 = gr.Group(visible=True, elem_classes="step-group")
1054
  with step1:
@@ -1057,19 +1069,16 @@ def create_interface():
1057
  help_btn = gr.Button("Find a Hospital by ZIP Code", variant="primary", size="lg")
1058
  know_btn = gr.Button("I Know My Hospital", variant="secondary", size="lg")
1059
 
1060
- gr.HTML(
1061
- '<div class="map-embed" style="margin-top:1rem;">'
1062
- '<iframe src="https://www.google.com/maps/d/u/0/embed?mid=1UVO9r7ZS26kZr8Q62dY0147v3Milu_s&ll=41.508444988744984%2C-72.7719992&z=9" '
1063
- 'width="100%" height="480" style="border:0; border-radius: 6px;" '
1064
- 'allowfullscreen="" loading="lazy" title="Map: Connecticut Hospital Locations"></iframe>'
1065
- '</div>'
1066
- )
1067
 
1068
  # Step 2a: Find hospital by ZIP
1069
  step2a = gr.Group(visible=False, elem_classes="step-group")
1070
  with step2a:
1071
  gr.Markdown("### Enter your ZIP code")
1072
  zip_input = gr.Textbox(label="ZIP Code", placeholder="e.g., 06511", max_lines=1)
 
 
 
 
1073
  search_btn = gr.Button("Search", variant="primary")
1074
 
1075
  nearby_cards = gr.Markdown()
@@ -1118,6 +1127,8 @@ def create_interface():
1118
  chatbot = gr.Chatbot(
1119
  label="Conversation",
1120
  height=350,
 
 
1121
  )
1122
 
1123
  # Suggested prompt buttons (pill-shaped via CSS)
@@ -1170,9 +1181,9 @@ def create_interface():
1170
  gr.update(visible=False)
1171
  ]
1172
 
1173
- def search_hospitals_wrapper(zip_code, lang):
1174
  """Geocode a ZIP and return nearby hospital cards and radio choices."""
1175
- cards, choices = find_nearby_hospitals(zip_code, lang)
1176
  if choices:
1177
  return cards, gr.update(choices=choices, visible=True, value=choices[0]), gr.update(visible=True)
1178
  return cards, gr.update(visible=False), gr.update(visible=False)
@@ -1257,13 +1268,65 @@ def create_interface():
1257
  ]
1258
 
1259
  def download_results(last_result, lang):
1260
- """Generate a temporary text file with the eligibility summary for download."""
1261
  if not last_result:
1262
  gr.Warning("No results to download." if lang == "en" else "No hay resultados para descargar.")
1263
  return gr.update(visible=False)
1264
- content = generate_download_content(last_result, lang)
1265
- tmp = tempfile.NamedTemporaryFile(mode='w', suffix='.txt', prefix='eligibility_summary_', delete=False)
1266
- tmp.write(content)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1267
  tmp.close()
1268
  return gr.update(value=tmp.name, visible=True)
1269
 
@@ -1330,7 +1393,7 @@ def create_interface():
1330
 
1331
  search_btn.click(
1332
  fn=search_hospitals_wrapper,
1333
- inputs=[zip_input, lang_state],
1334
  outputs=[nearby_cards, hospital_radio, continue_btn_a]
1335
  )
1336
 
 
16
  from openai import OpenAI
17
  from geopy.geocoders import Nominatim
18
  from geopy.distance import geodesic
19
+ from fpdf import FPDF
20
  import time
21
 
22
  OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
 
151
  "continue_button": "Continue",
152
  "back_button": "Back",
153
  "miles_away": "miles away",
154
+ "num_hospitals": "Number of hospitals to show",
155
  "free_care": "Free care up to",
156
  "sliding_scale": "Sliding scale up to",
157
  "status": "Status",
 
181
  "continue_button": "Continuar",
182
  "back_button": "Atrás",
183
  "miles_away": "millas de distancia",
184
+ "num_hospitals": "Número de hospitales a mostrar",
185
  "free_care": "Atención gratuita hasta",
186
  "sliding_scale": "Escala móvil hasta",
187
  "status": "Estado",
 
219
  _zip_geocode_cache[zip_code] = location
220
  return location
221
 
222
+ def find_nearby_hospitals(zip_code, lang, max_results=5):
223
+ """Find the nearest CT hospitals to a given ZIP code.
224
 
225
  Validates the ZIP is a CT format (060xx-069xx), geocodes it,
226
  then returns markdown cards and choice labels sorted by distance.
 
242
  distances.append((name, dist, data))
243
 
244
  distances.sort(key=lambda x: x[1])
245
+ nearest = distances[:int(max_results)]
246
 
247
  choices = []
248
  cards = ""
 
1035
  gr.HTML(
1036
  '<div class="oha-header">'
1037
  '<a href="https://portal.ct.gov/oha" target="_blank" rel="noopener">'
1038
+ '<h1>Office of the Healthcare Advocate</h1>'
1039
+ '<div class="oha-subtitle">Hospital Financial Assistance Eligibility Screener <span class="oha-beta">BETA</span></div>'
1040
  '</a>'
1041
  '</div>'
1042
  )
 
1052
  container=False
1053
  )
1054
 
1055
+ # Google Maps embed — placed outside step groups so it persists across all steps
1056
+ gr.HTML(
1057
+ '<div class="map-embed" style="margin-top:1rem;">'
1058
+ '<iframe src="https://www.google.com/maps/d/u/0/embed?mid=1UVO9r7ZS26kZr8Q62dY0147v3Milu_s&ll=41.508444988744984%2C-72.7719992&z=9" '
1059
+ 'width="100%" height="480" style="border:0; border-radius: 6px;" '
1060
+ 'allowfullscreen="" loading="lazy" title="Map: Connecticut Hospital Locations"></iframe>'
1061
+ '</div>'
1062
+ )
1063
+
1064
  # Step 1: Choose path
1065
  step1 = gr.Group(visible=True, elem_classes="step-group")
1066
  with step1:
 
1069
  help_btn = gr.Button("Find a Hospital by ZIP Code", variant="primary", size="lg")
1070
  know_btn = gr.Button("I Know My Hospital", variant="secondary", size="lg")
1071
 
 
 
 
 
 
 
 
1072
 
1073
  # Step 2a: Find hospital by ZIP
1074
  step2a = gr.Group(visible=False, elem_classes="step-group")
1075
  with step2a:
1076
  gr.Markdown("### Enter your ZIP code")
1077
  zip_input = gr.Textbox(label="ZIP Code", placeholder="e.g., 06511", max_lines=1)
1078
+ num_hospitals_slider = gr.Slider(
1079
+ minimum=3, maximum=9, value=5, step=1,
1080
+ label="Number of hospitals to show"
1081
+ )
1082
  search_btn = gr.Button("Search", variant="primary")
1083
 
1084
  nearby_cards = gr.Markdown()
 
1127
  chatbot = gr.Chatbot(
1128
  label="Conversation",
1129
  height=350,
1130
+ show_share_button=False,
1131
+ show_copy_button=True,
1132
  )
1133
 
1134
  # Suggested prompt buttons (pill-shaped via CSS)
 
1181
  gr.update(visible=False)
1182
  ]
1183
 
1184
+ def search_hospitals_wrapper(zip_code, lang, max_results):
1185
  """Geocode a ZIP and return nearby hospital cards and radio choices."""
1186
+ cards, choices = find_nearby_hospitals(zip_code, lang, max_results=max_results)
1187
  if choices:
1188
  return cards, gr.update(choices=choices, visible=True, value=choices[0]), gr.update(visible=True)
1189
  return cards, gr.update(visible=False), gr.update(visible=False)
 
1268
  ]
1269
 
1270
  def download_results(last_result, lang):
1271
+ """Generate a PDF with the eligibility summary for download."""
1272
  if not last_result:
1273
  gr.Warning("No results to download." if lang == "en" else "No hay resultados para descargar.")
1274
  return gr.update(visible=False)
1275
+ t = TRANSLATIONS[lang]
1276
+ data = last_result
1277
+
1278
+ pdf = FPDF()
1279
+ pdf.add_page()
1280
+ pdf.set_auto_page_break(auto=True, margin=20)
1281
+
1282
+ # Header
1283
+ pdf.set_font("Helvetica", "B", 18)
1284
+ pdf.set_text_color(0, 52, 120) # #003478
1285
+ pdf.cell(0, 12, "CT Hospital Financial Assistance", new_x="LMARGIN", new_y="NEXT")
1286
+ pdf.set_font("Helvetica", "", 12)
1287
+ pdf.set_text_color(33, 37, 41)
1288
+ pdf.cell(0, 8, "Eligibility Summary", new_x="LMARGIN", new_y="NEXT")
1289
+ pdf.ln(4)
1290
+
1291
+ # Hospital & Status
1292
+ pdf.set_font("Helvetica", "B", 14)
1293
+ pdf.cell(0, 10, data['hospital'], new_x="LMARGIN", new_y="NEXT")
1294
+ pdf.set_font("Helvetica", "B", 12)
1295
+ pdf.cell(0, 8, f"{t['status']}: {data['eligibility_status']}", new_x="LMARGIN", new_y="NEXT")
1296
+ pdf.ln(2)
1297
+
1298
+ # Details table
1299
+ pdf.set_font("Helvetica", "", 11)
1300
+ fields = [
1301
+ ("Annual Household Income" if lang == "en" else "Ingreso anual del hogar", f"${data['income']:,.0f}"),
1302
+ ("Household Size" if lang == "en" else "Tamaño del hogar", str(data['household_size'])),
1303
+ ("FPL Percentage" if lang == "en" else "Porcentaje FPL", f"{data['fpl_percentage']}%"),
1304
+ ("Discount Level" if lang == "en" else "Nivel de descuento", data['discount_level']),
1305
+ ("SNAP/WIC Enrolled" if lang == "en" else "Inscrito en SNAP/WIC", "Yes" if data['has_snap_wic'] else "No"),
1306
+ ("Has Insurance" if lang == "en" else "Tiene seguro", "Yes" if data.get('has_insurance') else "No"),
1307
+ ("PA 24-81 Eligible" if lang == "en" else "Elegible PA 24-81", "Yes" if data['pa_24_81_eligible'] else "No"),
1308
+ ]
1309
+ for label, value in fields:
1310
+ pdf.set_font("Helvetica", "B", 10)
1311
+ pdf.cell(80, 7, label)
1312
+ pdf.set_font("Helvetica", "", 10)
1313
+ pdf.cell(0, 7, value, new_x="LMARGIN", new_y="NEXT")
1314
+ pdf.ln(4)
1315
+
1316
+ # Contact
1317
+ pdf.set_font("Helvetica", "B", 11)
1318
+ pdf.cell(0, 8, f"Contact: {data['contact']}", new_x="LMARGIN", new_y="NEXT")
1319
+ if data['asset_limit']:
1320
+ pdf.cell(0, 8, f"Asset Limit: {data['asset_limit']}", new_x="LMARGIN", new_y="NEXT")
1321
+ pdf.ln(4)
1322
+
1323
+ # Disclaimer
1324
+ pdf.set_font("Helvetica", "I", 9)
1325
+ pdf.set_text_color(100, 100, 100)
1326
+ pdf.multi_cell(0, 5, t['disclaimer'])
1327
+
1328
+ tmp = tempfile.NamedTemporaryFile(suffix='.pdf', prefix='eligibility_summary_', delete=False)
1329
+ pdf.output(tmp.name)
1330
  tmp.close()
1331
  return gr.update(value=tmp.name, visible=True)
1332
 
 
1393
 
1394
  search_btn.click(
1395
  fn=search_hospitals_wrapper,
1396
+ inputs=[zip_input, lang_state, num_hospitals_slider],
1397
  outputs=[nearby_cards, hospital_radio, continue_btn_a]
1398
  )
1399
 
requirements.txt CHANGED
@@ -1,3 +1,4 @@
1
  gradio>=4.0
2
  openai>=1.0
3
  geopy>=2.4
 
 
1
  gradio>=4.0
2
  openai>=1.0
3
  geopy>=2.4
4
+ fpdf2>=2.7