File size: 3,780 Bytes
c4bbe32 d1ea62c c4bbe32 d1ea62c c4bbe32 d1ea62c c4bbe32 d1ea62c c4bbe32 d1ea62c c4bbe32 d1ea62c |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
import streamlit as st
import pandas as pd
from collections import defaultdict
import plotly.express as px
# Title and file upload
st.title("Enhanced Duty Roster Generator with Role-Specific Assignments")
uploaded_file = st.file_uploader("Upload your availability spreadsheet with roles", type=["csv", "xlsx"])
# Multi-shift configuration
shifts = st.multiselect("Select shifts for each day", ["Morning", "Afternoon", "Night"], ["Morning", "Afternoon"])
shift_requirements = {shift: st.number_input(f"Personnel required for {shift} shift", min_value=1, value=1) for shift in shifts}
if uploaded_file:
# Load the file
if uploaded_file.name.endswith('.csv'):
df = pd.read_csv(uploaded_file)
else:
df = pd.read_excel(uploaded_file)
# Display data and allow user to confirm roles
st.write("Personnel Availability and Roles Data:")
st.dataframe(df)
# Generate duty roster with roles
def generate_duty_roster_with_roles(data, shifts, shift_reqs):
roster = defaultdict(lambda: defaultdict(list))
for day in range(1, 31): # Example with 30 days
for shift in shifts:
role_counts = defaultdict(int)
assigned_today = 0
for _, row in data.iterrows():
name = row['Name']
role = row['Role']
unavailable_days = set(map(int, row['Unavailable Days'].split(',')))
if day not in unavailable_days and role_counts[role] < shift_reqs[shift]:
roster[day][shift].append((name, role))
role_counts[role] += 1
assigned_today += 1
if assigned_today == shift_reqs[shift]:
break
# If unable to meet required personnel, mark as unassigned
if assigned_today < shift_reqs[shift]:
roster[day][shift].append(("Unassigned", "N/A"))
return roster
# Generate and display the duty roster
duty_roster = generate_duty_roster_with_roles(df, shifts, shift_requirements)
# Display the roster with roles and shifts
st.write("Generated Duty Roster with Roles:")
for day, shifts in duty_roster.items():
st.write(f"Day {day}:")
for shift, personnel in shifts.items():
st.write(f"{shift} Shift: {', '.join([f'{p[0]} ({p[1]})' for p in personnel])}")
# Interactive Calendar Visualization
flattened_data = []
for day, shifts in duty_roster.items():
for shift, personnel in shifts.items():
for name, role in personnel:
flattened_data.append({"Day": day, "Shift": shift, "Personnel": name, "Role": role})
df_roster = pd.DataFrame(flattened_data)
fig = px.timeline(df_roster, x_start="Day", x_end="Day", y="Personnel", color="Shift", title="Duty Roster Calendar View")
st.plotly_chart(fig)
# Downloadable individual schedules
st.write("Download Individual Schedules:")
for person in df['Name'].unique():
person_schedule = df_roster[df_roster['Personnel'] == person]
person_csv = person_schedule.to_csv(index=False).encode('utf-8')
st.download_button(f"Download Schedule for {person}", data=person_csv, file_name=f"{person}_schedule.csv", mime="text/csv")
# Scheduling Insights and Analytics
st.write("Scheduling Insights:")
personnel_counts = df_roster.groupby("Personnel").size()
st.bar_chart(personnel_counts)
st.write(f"Total shifts assigned per person: {personnel_counts.to_dict()}")
st.write("Days with unassigned shifts:")
unassigned_days = df_roster[df_roster['Personnel'] == "Unassigned"]
st.write(unassigned_days[['Day', 'Shift']].drop_duplicates())
|