wanwanlin0521 commited on
Commit
0190c1e
·
verified ·
1 Parent(s): e257686

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +25 -27
app.py CHANGED
@@ -50,6 +50,8 @@ BOTTOM_MARGIN_MM = 5
50
  PRINTABLE_HEIGHT_MM = PAGE_HEIGHT_MM - TOP_MARGIN_MM - BOTTOM_MARGIN_MM # ≈ 290 mm
51
  # Approximate height of a single text line in the calendar layout (in mm)
52
  LINE_HEIGHT_MM = 0.90 # 6.8 pt ≈ 0.9 mm
 
 
53
  # ========================================== Shared CSS (Used in both HTML preview and PDF generation) ==========================================
54
  # CSS styles shared between the HTML preview and the PDF output.
55
  # Uses placeholders {{locations}}, {{start}}, {{end}}, {{time}} that are replaced at generation time.
@@ -348,7 +350,7 @@ def estimate_week_height(week_content, display_locs):
348
  max_lines = 0
349
  for day_html in week_content:
350
  content = day_html.split(' ', 1)[1] if ' ' in day_html else day_html
351
- lines = content.count(' ') + 1
352
  if lines > max_lines:
353
  max_lines = lines
354
  base_height = 4.0
@@ -923,9 +925,8 @@ def combine_schedules(provider_info_file, provider_files, ma_files, start_date,
923
  page_content = []
924
  week_content = []
925
  current_page_height = 0
926
- is_first_page = True
927
 
928
- # Static day headers (Mon-Sat)
929
  day_headers = """
930
  <div class="week day-headers">
931
  <div class="week-number"></div>
@@ -949,14 +950,17 @@ def combine_schedules(provider_info_file, provider_files, ma_files, start_date,
949
  if current.weekday() == 0 and len(week_content) > 0:
950
  finished_week = f'<div class="week"><div class="week-number">{week_num}</div>{"".join(week_content)}</div>'
951
  week_height = estimate_week_height(week_content, display_locs)
952
- needed = week_height + (5 if len(page_content) == 0 else 1)
953
-
954
- # Page break logic: start new page if adding this week would overflow
 
 
 
955
  if current_page_height + needed > PRINTABLE_HEIGHT_MM:
956
- html += f'<div class="page-group"><div class="week-group">{day_headers if is_first_page else ""}{"".join(page_content)}</div></div>'
 
957
  page_content = []
958
  current_page_height = 0
959
- is_first_page = False
960
 
961
  page_content.append(finished_week)
962
  current_page_height += needed
@@ -964,22 +968,21 @@ def combine_schedules(provider_info_file, provider_files, ma_files, start_date,
964
  week_num += 1
965
 
966
  # Build the HTML for the current day
967
- day_num = current.day
968
  day_html = f'<div class="day">{current.strftime("%m/%d")}'
969
 
970
- # Global conflict warnings (same provider at multiple locations)
971
  if perform_conflict:
972
  for p, locs, cls, msg in check_provider_location_conflicts(providers_df, current, display_locs):
973
  day_html += f'<div class="{cls}"><span class="warning-details">{msg}</span></div>'
974
 
975
- # === NEW: Check if any clinic has "CLINIC_CLOSE!" on this day ===
976
  has_clinic_close_anywhere = False
977
  if 'Note' in providers_df.columns:
978
  day_notes = providers_df[providers_df['Date'] == current]['Note'].dropna().astype(str).str.upper()
979
  if day_notes.str.contains('CLINIC_CLOSE').any():
980
  has_clinic_close_anywhere = True
981
 
982
- # Overall age coverage warning across main sites (only if no clinic is closed with "CLINIC_CLOSE!")
983
  if perform_overall and not has_clinic_close_anywhere:
984
  missing, _ = check_overall_age_coverage(providers_df, info_df, current, display_locs)
985
  if missing:
@@ -990,7 +993,7 @@ def combine_schedules(provider_info_file, provider_files, ma_files, start_date,
990
  loc_df = providers_df[(providers_df['Date'] == current) & (providers_df['Display_Location'] == loc)]
991
  loc_info = info_df[info_df['Location'] == loc]
992
 
993
- # Detect holiday or school-closed status from notes
994
  is_holiday = is_school = False
995
  if not loc_df.empty:
996
  notes = loc_df['Note'].dropna().str.strip().str.upper().tolist()
@@ -999,7 +1002,6 @@ def combine_schedules(provider_info_file, provider_files, ma_files, start_date,
999
  elif notes and all(n == 'SCHOOL CLOSED' for n in notes) and loc in NO_AGE_CHECK_LOCATIONS:
1000
  is_school = True
1001
 
1002
- # Remove completely empty rows
1003
  loc_df = loc_df[~((loc_df['Start_Time'].isna()) & (loc_df['End_Time'].isna()) & (loc_df['Note'].isna() | (loc_df['Note'] == '')))]
1004
 
1005
  if not loc_df.empty or is_holiday or is_school:
@@ -1009,7 +1011,6 @@ def combine_schedules(provider_info_file, provider_files, ma_files, start_date,
1009
  elif is_school:
1010
  day_html += '<div class="holiday-message">School Closed!</div>'
1011
  else:
1012
- # === NEW: Check for clinic closure ===
1013
  if is_clinic_closed(providers_df, current, loc):
1014
  day_html += '<div class="clinic-closed-warning">Clinic Closed!</div>'
1015
  else:
@@ -1020,8 +1021,6 @@ def combine_schedules(provider_info_file, provider_files, ma_files, start_date,
1020
  info_row = loc_info[loc_info['Provider'] == r['Name']]
1021
  name = info_row['Last_Name'].iloc[0] if not info_row.empty else r['Name']
1022
  tstr = get_time_string(r)
1023
-
1024
- # Color coding based on age coverage
1025
  if r['Name'] in full:
1026
  color = "#ff6347"
1027
  elif r['Name'] in under:
@@ -1038,17 +1037,14 @@ def combine_schedules(provider_info_file, provider_files, ma_files, start_date,
1038
  day_html += f'<span style="{style}">{name}: {tstr}</span><br>'
1039
  day_html += '</div>'
1040
 
1041
- # Operational hour coverage warning
1042
  if check_operation_coverage_flag and loc not in NO_OPERATION_CHECK_LOCATIONS:
1043
  gaps = check_operation_time_coverage(providers_df, current, loc)
1044
  if gaps:
1045
  day_html += f'<div class="operation-warning"><span class="warning-details">Missing: {", ".join(gaps)}</span></div>'
1046
 
1047
- # Per-location age coverage warning
1048
  if check_age_coverage_flag and missing and loc not in NO_AGE_CHECK_LOCATIONS:
1049
  day_html += f'<div class="warning"><span class="warning-details">Missing: {", ".join(missing)}</span></div>'
1050
 
1051
- # MA scheduling and ratio check
1052
  if check_ma_mismatch_flag:
1053
  ma_loc_df = ma_df[(ma_df['Date'] == current) & (ma_df['Display_Location'] == loc)]
1054
  ma_loc_df = ma_loc_df[~((ma_loc_df['Start_Time'].isna()) & (ma_loc_df['End_Time'].isna()) & (ma_loc_df['Note'].isna() | (ma_loc_df['Note'] == '')))]
@@ -1073,23 +1069,25 @@ def combine_schedules(provider_info_file, provider_files, ma_files, start_date,
1073
  week_content.append(day_html)
1074
  current += pd.Timedelta(days=1)
1075
 
1076
- # Finalize any remaining week
1077
  if week_content:
1078
  finished_week = f'<div class="week"><div class="week-number">{week_num}</div>{"".join(week_content)}</div>'
1079
  week_height = estimate_week_height(week_content, display_locs)
1080
- needed = week_height + (5 if len(page_content) == 0 else 1)
 
 
1081
  if current_page_height + needed > PRINTABLE_HEIGHT_MM:
1082
- html += f'<div class="page-group"><div class="week-group">{day_headers if is_first_page else ""}{"".join(page_content)}</div></div>'
1083
  page_content = []
1084
  current_page_height = 0
1085
- is_first_page = False
1086
  page_content.append(finished_week)
1087
 
1088
- # Close current page
1089
  if page_content:
1090
- html += f'<div class="page-group"><div class="week-group">{day_headers if is_first_page else ""}{"".join(page_content)}</div></div>'
1091
 
1092
- # Add weekly clinical hours summary table if requested
1093
  if show_weekly_hours:
1094
  wh, wt = calculate_weekly_hours(providers_df, info_df, start_obj, end_obj, display_locs)
1095
  html += '<div class="hours-table-section" style="break-before: page;">'
 
50
  PRINTABLE_HEIGHT_MM = PAGE_HEIGHT_MM - TOP_MARGIN_MM - BOTTOM_MARGIN_MM # ≈ 290 mm
51
  # Approximate height of a single text line in the calendar layout (in mm)
52
  LINE_HEIGHT_MM = 0.90 # 6.8 pt ≈ 0.9 mm
53
+ # Estimated height of the day headers block (in mm) — measured empirically
54
+ DAY_HEADERS_HEIGHT_MM = 5.0
55
  # ========================================== Shared CSS (Used in both HTML preview and PDF generation) ==========================================
56
  # CSS styles shared between the HTML preview and the PDF output.
57
  # Uses placeholders {{locations}}, {{start}}, {{end}}, {{time}} that are replaced at generation time.
 
350
  max_lines = 0
351
  for day_html in week_content:
352
  content = day_html.split(' ', 1)[1] if ' ' in day_html else day_html
353
+ lines = content.count('<br>') + content.count('</div>') + 1 # Better estimate using tags
354
  if lines > max_lines:
355
  max_lines = lines
356
  base_height = 4.0
 
925
  page_content = []
926
  week_content = []
927
  current_page_height = 0
 
928
 
929
+ # Static day headers (Mon-Sat) — now included on EVERY page
930
  day_headers = """
931
  <div class="week day-headers">
932
  <div class="week-number"></div>
 
950
  if current.weekday() == 0 and len(week_content) > 0:
951
  finished_week = f'<div class="week"><div class="week-number">{week_num}</div>{"".join(week_content)}</div>'
952
  week_height = estimate_week_height(week_content, display_locs)
953
+
954
+ # Add day headers height only if this is the first week on the current page
955
+ extra_header = DAY_HEADERS_HEIGHT_MM if len(page_content) == 0 else 0
956
+ needed = week_height + extra_header + 1 # +1 mm buffer between weeks
957
+
958
+ # Page break logic
959
  if current_page_height + needed > PRINTABLE_HEIGHT_MM:
960
+ # Close current page (always include headers)
961
+ html += f'<div class="page-group"><div class="week-group">{day_headers}{"".join(page_content)}</div></div>'
962
  page_content = []
963
  current_page_height = 0
 
964
 
965
  page_content.append(finished_week)
966
  current_page_height += needed
 
968
  week_num += 1
969
 
970
  # Build the HTML for the current day
 
971
  day_html = f'<div class="day">{current.strftime("%m/%d")}'
972
 
973
+ # Global conflict warnings
974
  if perform_conflict:
975
  for p, locs, cls, msg in check_provider_location_conflicts(providers_df, current, display_locs):
976
  day_html += f'<div class="{cls}"><span class="warning-details">{msg}</span></div>'
977
 
978
+ # Check for CLINIC_CLOSE note anywhere
979
  has_clinic_close_anywhere = False
980
  if 'Note' in providers_df.columns:
981
  day_notes = providers_df[providers_df['Date'] == current]['Note'].dropna().astype(str).str.upper()
982
  if day_notes.str.contains('CLINIC_CLOSE').any():
983
  has_clinic_close_anywhere = True
984
 
985
+ # Overall age coverage
986
  if perform_overall and not has_clinic_close_anywhere:
987
  missing, _ = check_overall_age_coverage(providers_df, info_df, current, display_locs)
988
  if missing:
 
993
  loc_df = providers_df[(providers_df['Date'] == current) & (providers_df['Display_Location'] == loc)]
994
  loc_info = info_df[info_df['Location'] == loc]
995
 
996
+ # Detect holiday or school-closed
997
  is_holiday = is_school = False
998
  if not loc_df.empty:
999
  notes = loc_df['Note'].dropna().str.strip().str.upper().tolist()
 
1002
  elif notes and all(n == 'SCHOOL CLOSED' for n in notes) and loc in NO_AGE_CHECK_LOCATIONS:
1003
  is_school = True
1004
 
 
1005
  loc_df = loc_df[~((loc_df['Start_Time'].isna()) & (loc_df['End_Time'].isna()) & (loc_df['Note'].isna() | (loc_df['Note'] == '')))]
1006
 
1007
  if not loc_df.empty or is_holiday or is_school:
 
1011
  elif is_school:
1012
  day_html += '<div class="holiday-message">School Closed!</div>'
1013
  else:
 
1014
  if is_clinic_closed(providers_df, current, loc):
1015
  day_html += '<div class="clinic-closed-warning">Clinic Closed!</div>'
1016
  else:
 
1021
  info_row = loc_info[loc_info['Provider'] == r['Name']]
1022
  name = info_row['Last_Name'].iloc[0] if not info_row.empty else r['Name']
1023
  tstr = get_time_string(r)
 
 
1024
  if r['Name'] in full:
1025
  color = "#ff6347"
1026
  elif r['Name'] in under:
 
1037
  day_html += f'<span style="{style}">{name}: {tstr}</span><br>'
1038
  day_html += '</div>'
1039
 
 
1040
  if check_operation_coverage_flag and loc not in NO_OPERATION_CHECK_LOCATIONS:
1041
  gaps = check_operation_time_coverage(providers_df, current, loc)
1042
  if gaps:
1043
  day_html += f'<div class="operation-warning"><span class="warning-details">Missing: {", ".join(gaps)}</span></div>'
1044
 
 
1045
  if check_age_coverage_flag and missing and loc not in NO_AGE_CHECK_LOCATIONS:
1046
  day_html += f'<div class="warning"><span class="warning-details">Missing: {", ".join(missing)}</span></div>'
1047
 
 
1048
  if check_ma_mismatch_flag:
1049
  ma_loc_df = ma_df[(ma_df['Date'] == current) & (ma_df['Display_Location'] == loc)]
1050
  ma_loc_df = ma_loc_df[~((ma_loc_df['Start_Time'].isna()) & (ma_loc_df['End_Time'].isna()) & (ma_loc_df['Note'].isna() | (ma_loc_df['Note'] == '')))]
 
1069
  week_content.append(day_html)
1070
  current += pd.Timedelta(days=1)
1071
 
1072
+ # Finalize remaining week
1073
  if week_content:
1074
  finished_week = f'<div class="week"><div class="week-number">{week_num}</div>{"".join(week_content)}</div>'
1075
  week_height = estimate_week_height(week_content, display_locs)
1076
+ extra_header = DAY_HEADERS_HEIGHT_MM if len(page_content) == 0 else 0
1077
+ needed = week_height + extra_header + 1
1078
+
1079
  if current_page_height + needed > PRINTABLE_HEIGHT_MM:
1080
+ html += f'<div class="page-group"><div class="week-group">{day_headers}{"".join(page_content)}</div></div>'
1081
  page_content = []
1082
  current_page_height = 0
1083
+
1084
  page_content.append(finished_week)
1085
 
1086
+ # Close final page
1087
  if page_content:
1088
+ html += f'<div class="page-group"><div class="week-group">{day_headers}{"".join(page_content)}</div></div>'
1089
 
1090
+ # Add weekly hours table
1091
  if show_weekly_hours:
1092
  wh, wt = calculate_weekly_hours(providers_df, info_df, start_obj, end_obj, display_locs)
1093
  html += '<div class="hours-table-section" style="break-before: page;">'