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())