resident-tally / app.py
ianpan's picture
Update app.py
88f716d verified
import datetime
import gradio as gr
import itertools
import numpy as np
import os
import pandas as pd
import re
from collections import defaultdict
def convert_date(d):
return datetime.datetime.strptime(str(d), "%B %d, %Y").date()
def main(files):
available_files = []
fname_dict = {os.path.basename(f).split(".")[0]: f for f in files}
for fname, fpath in fname_dict.items():
available_files.append(fname)
if fname == "qgenda_schedule":
schedule = pd.read_excel(fpath, header=None)
elif fname == "info":
core_rotations = pd.read_excel(fpath, sheet_name="Core Rotations", header=None)
call_rotations = pd.read_excel(fpath, sheet_name="Call Rotations", header=None)
holidays = pd.read_excel(fpath, sheet_name="Holidays", header=None)
pto = pd.read_excel(fpath, sheet_name="PTO", header=None)
academic_years = pd.read_excel(fpath, sheet_name="AYs")
call_shifts = pd.read_excel(fpath, sheet_name="Weekend Call Shifts", header=None)
roster = pd.read_excel(fpath, sheet_name="Resident Roster")
assert all(x in ["qgenda_schedule", "info"] for x in available_files)
# -- for debugging, load files from disk --
# Load in all files
# schedule = pd.read_excel("key_files/qgenda_schedule.xlsx", header=None) # remove first 3 rows
# core_rotations = pd.read_excel("key_files/rotations.xlsx", sheet_name="core", header=None)
# call_rotations = pd.read_excel("key_files/rotations.xlsx", sheet_name="call", header=None)
# holidays = pd.read_excel("key_files/holidays.xlsx", header=None)
# pto = pd.read_excel("key_files/pto.xlsx", header=None)
# academic_years = pd.read_excel("key_files/academic_years.xlsx")
# call_shifts = pd.read_excel("key_files/call_shifts.xlsx", header=None)
# roster = pd.read_excel("key_files/resident_roster.xlsx")
# --
schedule = schedule.iloc[3:].reset_index(drop=True) # remove first 3 rows
core_rotations = core_rotations.iloc[:, 0].tolist()
call_rotations = call_rotations.iloc[:, 0].tolist()
holidays = [_.date() for _ in holidays.iloc[:, 0].tolist()]
pto = pto.iloc[:, 0].tolist()
call_shifts = call_shifts.iloc[:, 0].tolist()
# remove stuff at the bottom
try:
try:
last_row = schedule.iloc[:, 0].tolist().index("Schedule Notes:")
except ValueError:
last_row = schedule.iloc[:, 0].tolist().index("Phone Numbers")
except ValueError:
last_row = len(schedule) + 1
schedule = schedule.iloc[:last_row].reset_index(drop=True)
# remove days of week rows
remove_rows = []
for idx, i in enumerate(schedule.iloc[:, 0].tolist()):
if i == "Sunday":
remove_rows.append(idx)
schedule = schedule.drop(remove_rows).reset_index(drop=True)
# split the schedule by weeks
sundays, week_rows = [], []
for idx, i in enumerate(schedule.iloc[:, 0].tolist()):
try:
tmp_date = convert_date(i)
sundays.append(tmp_date)
week_rows.append(idx)
except ValueError:
continue
weekly_schedule = {}
for idx, each_sunday in enumerate(sundays):
if idx == len(sundays) - 1:
weekly_schedule[each_sunday] = schedule.iloc[week_rows[idx]:].reset_index(drop=True)
else:
weekly_schedule[each_sunday] = schedule.iloc[week_rows[idx]:week_rows[idx+1]].reset_index(drop=True)
daily_schedule = {}
for each_week, each_schedule in weekly_schedule.items():
tmp_daily_schedule = {}
list_by_day = [each_schedule.iloc[:, idx:idx+2] for idx in range(0, each_schedule.shape[1], 2)]
for each_day in list_by_day:
tmp_date = convert_date(each_day.iloc[0, 0])
tmp_daily_schedule[tmp_date] = defaultdict(list)
tmp_residents, tmp_tasks = each_day.iloc[1:, 0].tolist(), each_day.iloc[1:, 1].tolist()
tmp_tasks = [re.sub(r" \([A-Z]+\)", "", str(_)) for _ in tmp_tasks]
# PTO supersedes everything else, meaning if you have a PTO task then all other tasks are void
pto_residents = []
for resident, task in zip(tmp_residents, tmp_tasks):
if task in pto:
pto_residents.append(resident)
for resident, task in zip(tmp_residents, tmp_tasks):
if str(resident) == "nan" or resident in pto_residents:
if task not in pto:
continue
if tmp_date.weekday() in [5, 6] and task in core_rotations + holidays:
continue
tmp_daily_schedule[tmp_date][resident].append(task)
daily_schedule.update(tmp_daily_schedule)
weekly_schedule_with_tasks = {}
for each_week, each_schedule in weekly_schedule.items():
tmp_weekly_schedule = {}
tmp_weekly_schedule[each_week] = defaultdict(list)
list_by_day = [each_schedule.iloc[:, idx:idx+2] for idx in range(0, each_schedule.shape[1], 2)]
for each_day in list_by_day:
tmp_date = convert_date(each_day.iloc[0, 0])
tmp_residents, tmp_tasks = each_day.iloc[1:, 0].tolist(), each_day.iloc[1:, 1].tolist()
tmp_tasks = [re.sub(r" \([A-Z]+\)", "", str(_)) for _ in tmp_tasks]
pto_residents = []
for resident, task in zip(tmp_residents, tmp_tasks):
if task in pto:
pto_residents.append(resident)
for resident, task in zip(tmp_residents, tmp_tasks):
if str(resident) == "nan" or resident in pto_residents:
continue
task = re.sub(r" \([A-Z]+\)", "", task)
if tmp_date.weekday() in [5, 6] and task in core_rotations:
continue
tmp_weekly_schedule[each_week][resident].append(task)
weekly_schedule_with_tasks.update(tmp_weekly_schedule)
# create a calendar for each resident
resident_calendar = defaultdict(dict)
for tmp_date, resident_dict in daily_schedule.items():
for resident_name, tasks in resident_dict.items():
resident_calendar[resident_name][tmp_date] = tasks
resident_weekly_calendar = defaultdict(dict)
for tmp_week, resident_dict in weekly_schedule_with_tasks.items():
for resident_name, tasks in resident_dict.items():
resident_weekly_calendar[resident_name][tmp_week] = tasks
# count number of weeks for each core rotation
resident_core_rotation_tallies = {}
for row_idx, row in academic_years.iterrows():
# need to subtract 1 day from start since it is a Monday and ED/NF/Consult start on Sunday so will affect
# those who start at the beginning of the year
start, stop = row["Start"].date() - datetime.timedelta(days=1), row["Stop"].date()
ay = row["Year"]
resident_core_rotation_tallies[ay] = defaultdict(dict)
for resident_name, calendar in resident_weekly_calendar.items():
tmp_rotation_counts = defaultdict(list)
tmp_calendar = {k: v for k, v in calendar.items() if k >= start and k < stop}
for each_week, weekly_tasks in tmp_calendar.items():
tmp_tasks, tmp_counts = np.unique(weekly_tasks, return_counts=True)
tmp_task_counts = {k: v for k, v in zip(tmp_tasks, tmp_counts)}
for k, v in tmp_task_counts.items():
if k in core_rotations and v >= 3:
tmp_rotation_counts[k].append(1)
tmp_rotation_counts = {k: np.sum(v) for k, v in tmp_rotation_counts.items()}
for each_core_rotation in core_rotations:
if each_core_rotation not in tmp_rotation_counts:
tmp_rotation_counts[each_core_rotation] = 0
resident_core_rotation_tallies[ay][resident_name] = tmp_rotation_counts
for each_ay, each_resident_dict in resident_core_rotation_tallies.items():
for each_resident, resident_rotation_counts in each_resident_dict.items():
resident_rotation_counts["Total Nucs"] = resident_rotation_counts["Nucs"] + resident_rotation_counts["CV Nucs"]
# it's easier to keep track of NF, consult by days rather than weeks due to potential random swaps, sick call, and split weeks
# also keep track of nucs days since for nucs time requirements days may be more important than weeks
resident_rotations_by_days = {}
for row_idx, row in academic_years.iterrows():
# need to subtract 1 day from start since it is a Monday and ED/NF/Consult start on Sunday so will affect
# those who start at the beginning of the year
start, stop = row["Start"].date() - datetime.timedelta(days=1), row["Stop"].date()
ay = row["Year"]
resident_rotations_by_days[ay] = defaultdict(dict)
for resident_name, calendar in resident_weekly_calendar.items():
tmp_rotation_counts = defaultdict(list)
tmp_calendar = {k: v for k, v in calendar.items() if k >= start and k < stop}
for each_week, weekly_tasks in tmp_calendar.items():
tmp_tasks, tmp_counts = np.unique(weekly_tasks, return_counts=True)
tmp_task_counts = {k: v for k, v in zip(tmp_tasks, tmp_counts)}
for k, v in tmp_task_counts.items():
if k in core_rotations + call_rotations:
tmp_rotation_counts[k].append(v)
tmp_rotation_counts = {k: np.sum(v) for k, v in tmp_rotation_counts.items()}
for each_core_rotation in core_rotations + call_rotations:
if each_core_rotation not in tmp_rotation_counts:
tmp_rotation_counts[each_core_rotation] = 0
resident_rotations_by_days[ay][resident_name] = tmp_rotation_counts
for each_ay, each_resident_dict in resident_rotations_by_days.items():
for each_resident, resident_rotation_counts in each_resident_dict.items():
resident_rotation_counts["Total Nucs"] = resident_rotation_counts["Nucs"] + resident_rotation_counts["CV Nucs"]
for each_ay, each_resident_dict in resident_rotations_by_days.items():
for each_resident, resident_rotation_counts in each_resident_dict.items():
resident_core_rotation_tallies[each_ay][each_resident]["ED"] = int(np.round(resident_rotation_counts["ED"] / 6))
resident_core_rotation_tallies[each_ay][each_resident]["Consult"] = int(np.round(resident_rotation_counts["Consult"] / 6))
resident_core_rotation_tallies[each_ay][each_resident]["NF"] = int(np.round(resident_rotation_counts["NF"] / 6))
# count weekend call shifts (Sat Consult, Sat NF, In-House, Sat EDD)
# does not include Sundays from NF, consult, ED rotation or Angio/IR call)
call_shifts_worked = {}
for row_idx, row in academic_years.iterrows():
# note here we do NOT subtract 1 day from start because if you are assigned to Sunday in-house shift
# prior to AY start it counts for the that AY
start, stop = row["Start"].date(), row["Stop"].date()
ay = row["Year"]
call_shifts_worked[ay] = defaultdict(dict)
for resident_name, calendar in resident_calendar.items(): # use daily calendar
tmp_call_shifts = defaultdict(list)
tmp_calendar = {k: v for k, v in calendar.items() if k >= start and k < stop}
for each_day, daily_tasks in tmp_calendar.items():
for each_task in daily_tasks:
if each_task in call_shifts:
tmp_call_shifts[each_task].append(1)
tmp_call_shifts = {k: np.sum(v) for k, v in tmp_call_shifts.items()}
for each_call_shift in call_shifts:
if each_call_shift not in tmp_call_shifts:
tmp_call_shifts[each_call_shift] = 0
call_shifts_worked[ay][resident_name] = tmp_call_shifts
# count holidays worked (call shifts only)
# differentiates between working on the actual holiday and holiday weekend
def count_holidays_worked(which_holidays):
holidays_worked = {}
for row_idx, row in academic_years.iterrows():
start, stop = row["Start"].date(), row["Stop"].date()
ay = row["Year"]
holidays_worked[ay] = defaultdict(dict)
for resident_name, calendar in resident_calendar.items():
tmp_holidays = []
tmp_calendar = {k: v for k, v in calendar.items() if k >= start and k < stop and k in which_holidays}
for each_holiday, daily_tasks in tmp_calendar.items():
for each_task in daily_tasks:
if each_task in call_rotations + call_shifts:
tmp_holidays.append(1)
break # if stack shifts, break so it doesn't double count
holidays_worked[ay][resident_name] = int(np.sum(tmp_holidays))
return holidays_worked
holidays_worked = count_holidays_worked(holidays)
# identify which holidays are Christmas, New Years, and Thanksgiving
# Thanksgiving is the only holiday in November
thanksgiving = [_ for _ in holidays if _.month == 11]
# consider observed dates as well
christmas = [_ for _ in holidays if _.month == 12 and abs(int((datetime.date(_.year, 12, 25) - _).days)) < 2]
new_years_candidates = [datetime.date(2000, 1, 1), datetime.date(2000, 12, 31), datetime.date(2000, 1, 2)]
new_years = []
for _ in holidays:
if _.month == 1 and _.day in [1, 2] or _.month == 12 and _.day == 31:
new_years.append(_)
thanksgiving_worked = count_holidays_worked(thanksgiving)
christmas_worked = count_holidays_worked(christmas)
new_years_worked = count_holidays_worked(new_years)
# also count weekends for Thanksgiving, Christmas, New Years
# if you work more than one day for a weekend, it will count for more than 1
# since thanksgiving is always Thurs, it's easy...
thanksgiving_weekend = []
for each_thanksgiving in thanksgiving:
tmp_weekend = []
for each_day in [*daily_schedule]:
if int((each_day - each_thanksgiving).days) in [2, 3]:
assert each_day.weekday() in [5, 6]
tmp_weekend.append(each_day)
thanksgiving_weekend.append(tmp_weekend)
# Christmas/NY a bit trickier
# first identify the Christmas and New Year's holiday weeks (AKA which weeks contain the observed holiday)
# remember- the week start always precedes the holiday
# make dict where key is week and value is all days in that week
weeks_dict = {}
for each_week in [*weekly_schedule]:
# week starts on Sunday
assert each_week.weekday() == 6
weeks_dict[each_week] = [each_week + datetime.timedelta(days=_) for _ in range(7)]
assert len(weeks_dict[each_week]) == 7
# there are 3 weekends here- weekend before Xmas holiday week, weekend in between, weekend after New Year's holiday week
# technically the middle one is the worst
# also for some people they may have Xmas holiday week off but then work weekend after New Year's holiday week
# which is really not that bad, but this is annoying to take into account so will just treat them all the same
christmas_weeks, new_years_weeks = [], []
for each_week, days_in_week in weeks_dict.items():
if len(list(set(days_in_week) & set(christmas))) > 0:
christmas_weeks.append(each_week)
if len(list(set(days_in_week) & set(new_years))) > 0:
new_years_weeks.append(each_week)
christmas_new_years_weekend = []
for each_week in christmas_weeks:
christmas_new_years_weekend.append([each_week, each_week - datetime.timedelta(days=1)]) # Sunday
for each_week in new_years_weeks:
christmas_new_years_weekend.append([each_week, each_week - datetime.timedelta(days=1)])
christmas_new_years_weekend.append([each_week + datetime.timedelta(days=7), each_week + datetime.timedelta(days=6)])
def count_holiday_weekends_worked(which_holidays):
holidays_worked = {}
for row_idx, row in academic_years.iterrows():
start, stop = row["Start"].date(), row["Stop"].date()
ay = row["Year"]
holidays_worked[ay] = defaultdict(dict)
for resident_name, calendar in resident_calendar.items():
tmp_holidays = []
tmp_calendar = {k: v for k, v in calendar.items() if k >= start and k < stop and k in list(itertools.chain(*which_holidays))}
tmp_weekend_task_dict = defaultdict(list)
for idx, each_weekend in enumerate(which_holidays):
for each_day, daily_tasks in tmp_calendar.items():
if each_day in each_weekend:
tmp_weekend_task_dict[idx].extend(daily_tasks)
for each_weekend, weekend_tasks in tmp_weekend_task_dict.items():
for each_task in weekend_tasks:
if each_task in call_rotations + call_shifts:
tmp_holidays.append(1)
break
holidays_worked[ay][resident_name] = int(np.sum(tmp_holidays))
return holidays_worked
thanksgiving_weekends_worked = count_holiday_weekends_worked(thanksgiving_weekend)
christmas_new_years_weekends_worked = count_holiday_weekends_worked(christmas_new_years_weekend)
# Count PTO days
pto_days = {}
for row_idx, row in academic_years.iterrows():
start, stop = row["Start"].date(), row["Stop"].date()
ay = row["Year"]
pto_days[ay] = defaultdict(dict)
for resident_name, calendar in resident_calendar.items(): # use daily calendar
tmp_pto_days = defaultdict(list)
tmp_calendar = {k: v for k, v in calendar.items() if k >= start and k < stop}
in_house_days = 0
for each_day, daily_tasks in tmp_calendar.items():
# doesn't count if weekend or holiday
for each_task in daily_tasks:
if each_task in pto and each_day.weekday() not in [5, 6]:
if each_task == "LOA":
# Easier to see how many LOA weeks if we just include holidays
tmp_pto_days[each_task].append(1)
elif each_task != "LOA" and each_day not in holidays:
tmp_pto_days[each_task].append(1)
if each_task == "In-House":
in_house_days += 1
tmp_pto_days = {k: np.sum(v) for k, v in tmp_pto_days.items()}
# treat VACATION and HOLIDAY the same then subtract in house days
for pto_day_type in pto:
if pto_day_type not in tmp_pto_days:
tmp_pto_days[pto_day_type] = 0
tmp_pto_days["VACATION"] = tmp_pto_days["VACATION"] + tmp_pto_days["HOLIDAY"] - in_house_days
_ = tmp_pto_days.pop("HOLIDAY")
pto_days[ay][resident_name] = tmp_pto_days
# Count sick call
sick_call_weekdays = {}
for row_idx, row in academic_years.iterrows():
start, stop = row["Start"].date(), row["Stop"].date()
ay = row["Year"]
sick_call_weekdays[ay] = defaultdict(dict)
for resident_name, calendar in resident_calendar.items():
tmp_sick_call_weekdays = []
tmp_calendar = {k: v for k, v in calendar.items() if k >= start and k < stop}
for each_day, daily_tasks in tmp_calendar.items():
if each_day.weekday() not in [0, 1, 2, 3]: # Mon-Thurs only
continue
if "Sick Call" in daily_tasks:
tmp_sick_call_weekdays.append(1)
sick_call_weekdays[ay][resident_name] = int(np.round(np.sum(tmp_sick_call_weekdays) / 4))
sick_call_weekends = {}
for row_idx, row in academic_years.iterrows():
start, stop = row["Start"].date(), row["Stop"].date()
ay = row["Year"]
sick_call_weekends[ay] = defaultdict(dict)
for resident_name, calendar in resident_calendar.items():
tmp_sick_call_weekends = []
tmp_calendar = {k: v for k, v in calendar.items() if k >= start and k < stop}
for each_day, daily_tasks in tmp_calendar.items():
if each_day.weekday() not in [4, 5, 6]: # Fri-Sun only
continue
if "Sick Call" in daily_tasks:
tmp_sick_call_weekends.append(1)
sick_call_weekends[ay][resident_name] = int(np.round(np.sum(tmp_sick_call_weekends) / 3))
sick_call_holidays = {}
for row_idx, row in academic_years.iterrows():
start, stop = row["Start"].date(), row["Stop"].date()
ay = row["Year"]
sick_call_holidays[ay] = defaultdict(dict)
for resident_name, calendar in resident_calendar.items():
tmp_sick_call_holidays = []
tmp_calendar = {k: v for k, v in calendar.items() if k >= start and k < stop}
for each_day, daily_tasks in tmp_calendar.items():
if each_day not in holidays:
continue
if "Sick Call" in daily_tasks:
tmp_sick_call_holidays.append(1)
sick_call_holidays[ay][resident_name] = int(np.sum(tmp_sick_call_holidays))
sick_call_major_holidays = {}
for row_idx, row in academic_years.iterrows():
start, stop = row["Start"].date(), row["Stop"].date()
ay = row["Year"]
sick_call_major_holidays[ay] = defaultdict(dict)
for resident_name, calendar in resident_calendar.items():
tmp_sick_call_major_holidays = defaultdict(list)
tmp_calendar = {k: v for k, v in calendar.items() if k >= start and k < stop}
for each_day, daily_tasks in tmp_calendar.items():
if each_day not in thanksgiving + christmas + new_years:
continue
if "Sick Call" in daily_tasks:
if each_day in thanksgiving:
tmp_sick_call_major_holidays["Thanksgiving"].append(1)
elif each_day in christmas:
tmp_sick_call_major_holidays["Xmas"].append(1)
elif each_day in new_years:
tmp_sick_call_major_holidays["NY"].append(1)
for each_major_holiday in ["Thanksgiving", "Xmas", "NY"]:
if each_major_holiday not in tmp_sick_call_major_holidays:
tmp_sick_call_major_holidays[each_major_holiday] = 0
for k, v in tmp_sick_call_major_holidays.items():
tmp_sick_call_major_holidays[k] = int(np.sum(v))
sick_call_major_holidays[ay][resident_name] = tmp_sick_call_major_holidays
sick_call_combined = sick_call_major_holidays.copy()
for ay, residents in sick_call_combined.items():
for resident_name, resident_dict in residents.items():
sick_call_combined[ay][resident_name]["Weekdays"] = sick_call_weekdays[ay][resident_name]
sick_call_combined[ay][resident_name]["Weekends"] = sick_call_weekends[ay][resident_name]
sick_call_combined[ay][resident_name]["Holidays"] = sick_call_holidays[ay][resident_name]
# Eliminate residents from years they were not a resident yet
for each_ay, each_resident_dict in resident_core_rotation_tallies.copy().items():
for each_resident, resident_rotation_dict in each_resident_dict.copy().items():
if np.sum(list(resident_rotation_dict.values())) == 0:
_ = resident_core_rotation_tallies[each_ay].pop(each_resident)
classes = defaultdict(list)
for row_idx, row in roster.iterrows():
classes[row.Year].append(row.Resident)
######################################################
# CREATE EXCEL SPREADSHEET FOR CORE ROTATION TALLIES #
######################################################
core_rotation_tallies_by_class = {}
for each_class, residents in classes.items():
core_rotation_tallies_by_class[each_class] = {}
for each_resident in residents:
tmp_df_list = []
for each_ay, ay_tallies in resident_core_rotation_tallies.items():
if each_resident not in ay_tallies:
continue
tmp_df = pd.DataFrame(ay_tallies[each_resident], index=[0])
tmp_df["Year"] = each_ay
tmp_df_list.append(tmp_df)
tmp_df = pd.concat(tmp_df_list)
tmp_total = dict(tmp_df.sum())
tmp_total["Year"] = "Total"
filler = {k: "" for k in [*tmp_total]}
filler = pd.DataFrame(filler, index=[0])
tmp_df = pd.concat([tmp_df, pd.DataFrame(tmp_total, index=[0]), filler])
tmp_df["Resident"] = [each_resident] + [""] * (len(tmp_df) - 1)
tmp_df = tmp_df[["Resident", "Year"] + list(tmp_df.columns[:-2])]
core_rotation_tallies_by_class[each_class][each_resident] = tmp_df
core_rotation_tallies_df_by_class = {}
for each_class in [*core_rotation_tallies_by_class]:
core_rotation_tallies_df_by_class[each_class] = pd.concat(list(core_rotation_tallies_by_class[each_class].values()))
####################################################
# CREATE EXCEL SPREADSHEET FOR WEEKEND CALL SHIFTS #
####################################################
weekend_call_shifts_by_class = {}
for each_class, residents in classes.items():
weekend_call_shifts_by_class[each_class] = {}
for each_resident in residents:
tmp_df_list = []
for each_ay, ay_tallies in call_shifts_worked.items():
if each_resident not in ay_tallies:
continue
tmp_df = pd.DataFrame(ay_tallies[each_resident], index=[0])
tmp_df["Year"] = each_ay
tmp_df_list.append(tmp_df)
tmp_df = pd.concat(tmp_df_list)
tmp_total = dict(tmp_df.sum())
tmp_total["Year"] = "Total"
filler = {k: "" for k in [*tmp_total]}
filler = pd.DataFrame(filler, index=[0])
tmp_df = pd.concat([tmp_df, pd.DataFrame(tmp_total, index=[0]), filler])
tmp_df["Resident"] = [each_resident] + [""] * (len(tmp_df) - 1)
tmp_df = tmp_df[["Resident", "Year"] + list(tmp_df.columns[:-2])]
weekend_call_shifts_by_class[each_class][each_resident] = tmp_df
weekend_call_shifts_df_by_class = {}
for each_class in [*weekend_call_shifts_by_class]:
weekend_call_shifts_df_by_class[each_class] = pd.concat(list(weekend_call_shifts_by_class[each_class].values()))
weekend_call_shifts_df = pd.concat([v for k, v in weekend_call_shifts_df_by_class.items()])
#################################################
# CREATE EXCEL SPREADSHEET FOR SICK CALL SHIFTS #
#################################################
sick_call_by_class = {}
for each_class, residents in classes.items():
sick_call_by_class[each_class] = {}
for each_resident in residents:
tmp_df_list = []
for each_ay, ay_tallies in sick_call_combined.items():
if each_resident not in ay_tallies:
continue
tmp_df = pd.DataFrame(ay_tallies[each_resident], index=[0])
tmp_df = tmp_df[["Weekdays", "Weekends", "Holidays", "Thanksgiving", "Xmas", "NY"]]
tmp_df["Year"] = each_ay
tmp_df_list.append(tmp_df)
tmp_df = pd.concat(tmp_df_list)
tmp_total = dict(tmp_df.sum())
tmp_total["Year"] = "Total"
filler = {k: "" for k in [*tmp_total]}
filler = pd.DataFrame(filler, index=[0])
tmp_df = pd.concat([tmp_df, pd.DataFrame(tmp_total, index=[0]), filler])
tmp_df["Resident"] = [each_resident] + [""] * (len(tmp_df) - 1)
tmp_df = tmp_df[["Resident", "Year"] + list(tmp_df.columns[:-2])]
sick_call_by_class[each_class][each_resident] = tmp_df
sick_call_df_by_class = {}
for each_class in [*sick_call_by_class]:
sick_call_df_by_class[each_class] = pd.concat(list(sick_call_by_class[each_class].values()))
sick_call_df = pd.concat([v for k, v in sick_call_df_by_class.items()])
#########################################
# CREATE EXCEL SPREADSHEET FOR PTO DAYS #
#########################################
pto_by_class = {}
for each_class, residents in classes.items():
pto_by_class[each_class] = {}
for each_resident in residents:
tmp_df_list = []
for each_ay, ay_pto in pto_days.items():
if each_resident not in ay_tallies:
continue
tmp_df = pd.DataFrame(ay_pto[each_resident], index=[0])
tmp_df["Total (Days)"] = tmp_df.sum(1)
tmp_df["Total (Weeks)"] = np.round(tmp_df["Total (Days)"] / 5).astype("int")
tmp_df["Year"] = each_ay
tmp_df_list.append(tmp_df)
tmp_df = pd.concat(tmp_df_list)
tmp_total = dict(tmp_df.sum())
tmp_total["Year"] = "Total"
filler = {k: "" for k in [*tmp_total]}
filler = pd.DataFrame(filler, index=[0])
tmp_df = pd.concat([tmp_df, pd.DataFrame(tmp_total, index=[0]), filler])
tmp_df["Resident"] = [each_resident] + [""] * (len(tmp_df) - 1)
tmp_df = tmp_df[["Resident", "Year"] + list(tmp_df.columns[:-2])]
pto_by_class[each_class][each_resident] = tmp_df
pto_df_by_class = {}
for each_class in [*pto_by_class]:
pto_df_by_class[each_class] = pd.concat(list(pto_by_class[each_class].values()))
pto_df = pd.concat([v for k, v in pto_df_by_class.items()])
#########################################
# CREATE EXCEL SPREADSHEET FOR HOLIDAYS #
#########################################
# first, combine the separate holiday dicts into 1...
combined_holidays = {}
for each_ay, residents in holidays_worked.items():
combined_holidays[each_ay] = defaultdict(dict)
for resident_name, resident_dict in residents.items():
combined_holidays[each_ay][resident_name] = {
"All Holidays": holidays_worked[each_ay][resident_name],
"Thanksgiving": thanksgiving_worked[each_ay][resident_name],
"Xmas": christmas_worked[each_ay][resident_name],
"NY": new_years_worked[each_ay][resident_name],
"Thanksgiving Weekend": thanksgiving_weekends_worked[each_ay][resident_name],
"Xmas/NY Weekend": christmas_new_years_weekends_worked[each_ay][resident_name]
}
holidays_by_class = {}
for each_class, residents in classes.items():
holidays_by_class[each_class] = {}
for each_resident in residents:
tmp_df_list = []
for each_ay, ay_holidays in combined_holidays.items():
if each_resident not in ay_holidays:
continue
tmp_df = pd.DataFrame(ay_holidays[each_resident], index=[0])
tmp_df["Year"] = each_ay
tmp_df_list.append(tmp_df)
tmp_df = pd.concat(tmp_df_list)
tmp_total = dict(tmp_df.sum())
tmp_total["Year"] = "Total"
filler = {k: "" for k in [*tmp_total]}
filler = pd.DataFrame(filler, index=[0])
tmp_df = pd.concat([tmp_df, pd.DataFrame(tmp_total, index=[0]), filler])
tmp_df["Resident"] = [each_resident] + [""] * (len(tmp_df) - 1)
tmp_df = tmp_df[["Resident", "Year"] + list(tmp_df.columns[:-2])]
holidays_by_class[each_class][each_resident] = tmp_df
holidays_df_by_class = {}
for each_class in [*holidays_by_class]:
holidays_df_by_class[each_class] = pd.concat(list(holidays_by_class[each_class].values()))
holidays_df = pd.concat([v for k, v in holidays_df_by_class.items()])
with pd.ExcelWriter("all_tallies.xlsx") as writer:
for each_class, each_df in core_rotation_tallies_df_by_class.items():
each_df.to_excel(writer, sheet_name=f"R{each_class}", index=False)
pto_df.to_excel(writer, sheet_name="PTO", index=False)
weekend_call_shifts_df.to_excel(writer, sheet_name=f"Call Shifts", index=False)
sick_call_df.to_excel(writer, sheet_name=f"Sick Call", index=False)
holidays_df.to_excel(writer, sheet_name="Holidays", index=False)
return "all_tallies.xlsx"
demo = gr.Interface(
fn=main,
inputs=gr.File(file_count="multiple"),
outputs="file")
if __name__ == "__main__":
demo.launch()