Spaces:
Sleeping
Sleeping
import streamlit as st | |
import matplotlib.pyplot as plt | |
import numpy as np | |
import pandas as pd | |
import matplotlib.dates as mdates | |
import seaborn as sns | |
from io import BytesIO | |
from datetime import datetime, timedelta | |
import plotly.graph_objects as go | |
import plotly.express as px | |
from matplotlib.colors import LinearSegmentedColormap | |
from wordcloud import WordCloud | |
# Streamlit app title and tab configuration | |
st.set_page_config(page_title="Project Timeline Manager", layout="wide") | |
st.title("Project Timeline Management App") | |
# Create tabs for different chart types | |
tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs([ | |
"Progress Bar Chart", | |
"Timeline Chart", | |
"Progress Pie Chart", | |
"Gantt Chart", | |
"Resource Allocation", | |
"Executive Dashboard" | |
]) | |
# Sidebar project details | |
st.sidebar.header("Project Details") | |
project_name = st.sidebar.text_input("Project Name", "Sample Project", key="project_name") | |
total_duration = st.sidebar.slider("Total Project Duration (Months)", 1, 24, 12, key="total_duration") | |
# Project start date for Gantt chart | |
start_date = st.sidebar.date_input("Project Start Date", datetime.now(), key="start_date") | |
# Number of tasks | |
task_count = st.sidebar.number_input("Number of Tasks", min_value=1, max_value=10, value=3, key="task_count") | |
tasks = [] | |
task_colors = [] | |
timeline_tasks = [] | |
gantt_tasks = [] | |
resource_tasks = [] | |
# Font Properties - Common for all charts | |
st.sidebar.header("Font Settings") | |
font_properties = { | |
'axis_label_size': st.sidebar.slider("Axis Label Font Size", 8, 20, 12, key="axis_label_size"), | |
'tick_size': st.sidebar.slider("Tick Font Size", 6, 20, 10, key="tick_size"), | |
'title_size': st.sidebar.slider("Title Font Size", 10, 24, 14, key="title_size"), | |
'legend_size': st.sidebar.slider("Legend Font Size", 8, 20, 12, key="legend_size") | |
} | |
# Set font to Times New Roman | |
plt.rcParams['font.family'] = 'Times New Roman' | |
# Theme selection | |
theme_options = ["Default", "Business", "Modern", "Classic", "Vibrant"] | |
selected_theme = st.sidebar.selectbox("Color Theme", theme_options, key="color_theme") | |
# Theme color palettes | |
theme_colors = { | |
"Default": ["#4e79a7", "#f28e2c", "#e15759", "#76b7b2", "#59a14f", "#edc949", "#af7aa1", "#ff9da7", "#9c755f", "#bab0ab"], | |
"Business": ["#1f77b4", "#aec7e8", "#ff7f0e", "#ffbb78", "#2ca02c", "#98df8a", "#d62728", "#ff9896", "#9467bd", "#c5b0d5"], | |
"Modern": ["#636EFA", "#EF553B", "#00CC96", "#AB63FA", "#FFA15A", "#19D3F3", "#FF6692", "#B6E880", "#FF97FF", "#FECB52"], | |
"Classic": ["#5A8CA8", "#F2A45C", "#8ABD5A", "#C15B5B", "#9D7ABD", "#7EC9C3", "#D67AB1", "#FDC55B", "#9E765F", "#B3B3B3"], | |
"Vibrant": ["#ff3f3f", "#ffa51f", "#faff1f", "#1fff49", "#1fcaff", "#881fff", "#ff1fca", "#8b8b8b", "#ff871f", "#1fffb6"] | |
} | |
current_theme = theme_colors[selected_theme] | |
# Task details collection | |
for i in range(task_count): | |
with st.sidebar.expander(f"Task {i+1} Details"): | |
# Common task details | |
task_name = st.text_input(f"Task {i+1} Name", f"Task {i+1}", key=f"task_name_{i}") | |
# Progress Bar Chart details | |
expected_duration = st.slider(f"Expected Duration (Months)", 1, total_duration, | |
min(3, total_duration), key=f"expected_duration_{i}") | |
completed_percentage = st.slider(f"Completion Percentage", 0, 100, 30, | |
key=f"completion_percentage_{i}") | |
# Use theme colors or custom colors | |
use_theme_color = st.checkbox("Use Theme Colors", True, key=f"use_theme_color_{i}") | |
if use_theme_color: | |
task_color = current_theme[i % len(current_theme)] | |
completed_color = adjust_color_brightness(task_color, 0.7) # Lighter version | |
else: | |
task_color = st.color_picker(f"Bar Color", f"#{np.random.randint(0, 0xFFFFFF):06x}", | |
key=f"task_color_{i}") | |
completed_color = st.color_picker(f"Completion Color", f"#{np.random.randint(0, 0xFFFFFF):06x}", | |
key=f"completed_color_{i}") | |
# Timeline Chart details | |
start_month = st.selectbox(f"Start Month", list(range(1, 13)), index=min(i, 11), | |
key=f"start_month_{i}") | |
end_month = st.selectbox(f"End Month", list(range(1, 13)), index=min(i+2, 11), | |
key=f"end_month_{i}") | |
duration = end_month - start_month + 1 | |
# Gantt Chart details | |
task_start_date = start_date + timedelta(days=30*start_month) | |
task_end_date = start_date + timedelta(days=30*end_month) | |
# Resource allocation details | |
resource_name = st.text_input(f"Resource Owner", f"Resource {i+1}", key=f"resource_name_{i}") | |
resource_allocation = st.slider(f"Resource Allocation (%)", 0, 100, 50, key=f"resource_allocation_{i}") | |
priority = st.selectbox(f"Priority", ["Low", "Medium", "High"], index=1, key=f"priority_{i}") | |
# Dependencies (for Gantt chart) | |
if i > 0: | |
depends_on = st.multiselect(f"Depends on", [f"Task {j+1}" for j in range(i)], | |
key=f"depends_on_{i}") | |
else: | |
depends_on = [] | |
# Risk level | |
risk_level = st.selectbox(f"Risk Level", ["Low", "Medium", "High"], index=0, key=f"risk_level_{i}") | |
# Add to respective task lists | |
tasks.append((task_name, expected_duration, completed_percentage)) | |
task_colors.append((task_color, completed_color)) | |
timeline_tasks.append((task_name, start_month, end_month, duration, completed_percentage)) | |
gantt_tasks.append((task_name, task_start_date, task_end_date, completed_percentage, depends_on, risk_level)) | |
resource_tasks.append((task_name, resource_name, resource_allocation, priority, risk_level)) | |
# Progress Bar Chart Customization | |
with st.sidebar.expander("Progress Bar Chart Settings"): | |
bar_width = st.slider("Bar Width", 0.1, 1.0, 0.6, 0.1, key="bar_width") | |
completion_bar_width = st.slider("Completion Bar Width", 0.1, 1.0, 0.4, 0.1, key="completion_bar_width") | |
progress_text_position = st.selectbox("Completion % Position", | |
["on the bar", "left side of the bar", "right side of the bar"], | |
key="progress_text_position") | |
progress_text_size = st.slider("Progress Text Size", 6, 20, 10, key="progress_text_size") | |
show_grid = st.checkbox("Show Grid", True, key="show_grid") | |
grid_style = st.selectbox("Grid Style", ["solid", "dashed", "dotted", "dashdot"], key="grid_style") | |
show_critical_path = st.checkbox("Highlight Critical Path", False, key="show_critical_path") | |
# Timeline Chart Customization | |
with st.sidebar.expander("Timeline Chart Settings"): | |
timeline_bar_width = st.slider("Timeline Bar Width (%)", 50, 100, 80, 5, key="timeline_bar_width") / 100 | |
show_milestones = st.checkbox("Show Milestones", True, key="show_milestones") | |
milestone_style = st.selectbox("Milestone Style", ["star", "diamond", "triangle", "circle"], | |
key="milestone_style") | |
show_today_line = st.checkbox("Show Today Line", True, key="show_today_line") | |
today_line_style = st.selectbox("Today Line Style", ["solid", "dashed", "dotted"], key="today_line_style") | |
# Pie Chart Customization | |
with st.sidebar.expander("Pie Chart Settings"): | |
# Donut chart settings | |
gap_size = st.slider("Gap Between Donuts", 0.0, 0.2, 0.1, key="gap_size") | |
border_line = st.color_picker("Border Line Color", "#000000", key="border_line") | |
show_inner_pie = st.checkbox("Show Inner Pie Chart", True, key="show_inner_pie") | |
show_outer_pie = st.checkbox("Show Outer Pie Chart", True, key="show_outer_pie") | |
pie_text_size = st.slider("Pie Chart Text Size", 6, 20, 12, key="pie_text_size") | |
pie_text_orientation = st.selectbox("Text Orientation", | |
["font print in horizental", "font print in vertical", | |
"font print in circular"], | |
key="pie_text_orientation") | |
explode_largest = st.checkbox("Explode Largest Segment", False, key="explode_largest") | |
use_3d_pie = st.checkbox("Use 3D Pie", False, key="use_3d_pie") | |
# Gantt Chart Customization | |
with st.sidebar.expander("Gantt Chart Settings"): | |
gantt_height = st.slider("Chart Height", 300, 800, 500, key="gantt_height") | |
show_dependencies = st.checkbox("Show Dependencies", True, key="show_dependencies") | |
critical_path_color = st.color_picker("Critical Path Color", "#FF0000", key="critical_path_color") | |
weekend_color = st.color_picker("Weekend Highlight", "#f0f0f0", key="weekend_color") | |
milestone_color = st.color_picker("Milestone Color", "#FFD700", key="milestone_color") | |
show_resource_labels = st.checkbox("Show Resource Labels", True, key="show_resource_labels") | |
risk_highlighting = st.checkbox("Highlight Risks", True, key="risk_highlighting") | |
# Resource Allocation Chart Settings | |
with st.sidebar.expander("Resource Allocation Settings"): | |
resource_chart_type = st.selectbox("Chart Type", | |
["Stacked Bar", "Heatmap", "Bubble Chart"], | |
key="resource_chart_type") | |
show_overallocation = st.checkbox("Highlight Overallocation", True, key="show_overallocation") | |
overallocation_threshold = st.slider("Overallocation Threshold (%)", 80, 120, 100, | |
key="overallocation_threshold") | |
group_by = st.selectbox("Group Resources By", ["None", "Priority", "Risk Level"], | |
key="group_by") | |
# Executive Dashboard Settings | |
with st.sidebar.expander("Executive Dashboard Settings"): | |
kpi_columns = st.slider("KPI Columns", 2, 4, 3, key="kpi_columns") | |
show_word_cloud = st.checkbox("Show Word Cloud", True, key="show_word_cloud") | |
show_burndown = st.checkbox("Show Burndown Chart", True, key="show_burndown") | |
show_velocity = st.checkbox("Show Velocity Chart", True, key="show_velocity") | |
velocity_periods = st.slider("Velocity Periods", 2, 10, 5, key="velocity_periods") | |
burndown_style = st.selectbox("Burndown Style", ["Linear", "Ideal", "Custom"], key="burndown_style") | |
dashboard_layout = st.selectbox("Dashboard Layout", ["Grid", "Vertical", "Horizontal"], | |
key="dashboard_layout") | |
# Legend Position (common for all charts) | |
legend_position_options = ["top right", "top midle", "top left", | |
"mid right", "mid midle", "mid left", | |
"bottom right", "bottom midle", "bottom left"] | |
legend_position = st.sidebar.selectbox("Legend Position", legend_position_options, key="legend_position") | |
# Map legend positions to matplotlib | |
legend_loc_map = { | |
"top right": "upper right", "top midle": "upper center", "top left": "upper left", | |
"mid right": "center right", "mid midle": "center", "mid left": "center left", | |
"bottom right": "lower right", "bottom midle": "lower center", "bottom left": "lower left" | |
} | |
# Brochure and Presentation Export Options | |
with st.sidebar.expander("Export Options"): | |
export_format = st.selectbox("Export Format", ["PNG", "PDF", "SVG", "HTML"], key="export_format") | |
export_dpi = st.slider("Export DPI", 72, 600, 300, key="export_dpi") | |
include_header = st.checkbox("Include Header/Footer", True, key="include_header") | |
include_logo = st.checkbox("Include Logo", False, key="include_logo") | |
page_size = st.selectbox("Page Size", ["Letter", "A4", "A3", "Legal"], key="page_size") | |
orientation = st.selectbox("Orientation", ["Landscape", "Portrait"], key="orientation") | |
# Helper Functions | |
def adjust_color_brightness(hex_color, factor): | |
"""Adjust the brightness of a hex color by a factor.""" | |
# Convert hex to RGB | |
if hex_color.startswith('#'): | |
hex_color = hex_color[1:] | |
rgb = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4)) | |
# Adjust brightness | |
rgb_adjusted = [min(255, int(c * factor)) for c in rgb] | |
# Convert back to hex | |
return f"#{rgb_adjusted[0]:02x}{rgb_adjusted[1]:02x}{rgb_adjusted[2]:02x}" | |
# Function to create a dual-bar chart for each task | |
def create_dual_bar_chart(tasks, bar_width, task_colors, common_x_range, font_properties, | |
completion_bar_width, progress_text_position, progress_text_size): | |
fig, ax = plt.subplots(figsize=(10, 6)) | |
for i, (task_name, expected_duration, completed_percentage) in enumerate(tasks): | |
task_color = task_colors[i][0] | |
completed_color = task_colors[i][1] | |
# Dark bar for expected duration | |
ax.barh(i, expected_duration, color=task_color, height=bar_width, | |
label='Expected Duration' if i == 0 else "") | |
# Light bar for completed percentage with adjustable width | |
completed_width = expected_duration * (completed_percentage / 100) | |
ax.barh(i, completed_width, color=completed_color, height=completion_bar_width, | |
label='Completed Percentage' if i == 0 else "") | |
# Add completion percentage text | |
if progress_text_position == "on the bar": | |
ax.text(completed_width/2, i, f"{completed_percentage}%", | |
ha='center', va='center', fontsize=progress_text_size, fontname='Times New Roman', | |
color='black' if completed_percentage < 50 else 'white') | |
elif progress_text_position == "left side of the bar": | |
ax.text(0 - 0.5, i, f"{completed_percentage}%", | |
ha='right', va='center', fontsize=progress_text_size, fontname='Times New Roman') | |
elif progress_text_position == "right side of the bar": | |
ax.text(expected_duration + 0.5, i, f"{completed_percentage}%", | |
ha='left', va='center', fontsize=progress_text_size, fontname='Times New Roman') | |
# Add grid if enabled | |
if show_grid: | |
ax.grid(True, linestyle=grid_style, alpha=0.7) | |
# Highlight critical path if enabled | |
if show_critical_path: | |
# Find critical tasks (for demo, we'll consider the longest tasks as critical) | |
critical_tasks = sorted(enumerate(tasks), key=lambda x: x[1][1], reverse=True)[:2] | |
for idx, _ in critical_tasks: | |
ax.get_children()[idx].set_edgecolor('red') | |
ax.get_children()[idx].set_linewidth(2) | |
ax.set_xlim(common_x_range) # Common x-axis range | |
ax.set_xlabel('Months', fontsize=font_properties['axis_label_size'], fontname='Times New Roman') | |
ax.set_title('Task Completion Progress', fontsize=font_properties['title_size'], fontname='Times New Roman') | |
# Customize axis ticks | |
ax.tick_params(axis='both', labelsize=font_properties['tick_size']) | |
# Set the y-axis labels to task names | |
ax.set_yticks(range(len(tasks))) | |
ax.set_yticklabels([task[0] for task in tasks], fontsize=font_properties['tick_size'], fontname='Times New Roman') | |
# Customize legend with selected position | |
ax.legend(fontsize=font_properties['legend_size'], loc=legend_loc_map[legend_position]) | |
plt.tight_layout() | |
return fig | |
# Function to create a timeline chart | |
def plot_timeline_chart(tasks, bar_width, font_properties, progress_text_position, progress_text_size): | |
fig, ax = plt.subplots(figsize=(10, 6)) | |
colors = [current_theme[i % len(current_theme)] for i in range(len(tasks))] | |
for i, (task_name, start, end, duration, completion) in enumerate(tasks): | |
ax.barh(i, duration, left=start, color=colors[i], height=bar_width) | |
# Add completion percentage based on selected position | |
if progress_text_position == "on the bar": | |
ax.text(start + duration/2, i, f"{completion}%", | |
ha='center', va='center', fontsize=progress_text_size, fontname='Times New Roman', | |
color='black' if completion < 50 else 'white') | |
elif progress_text_position == "left side of the bar": | |
ax.text(start - 0.5, i, f"{completion}%", | |
ha='right', va='center', fontsize=progress_text_size, fontname='Times New Roman') | |
elif progress_text_position == "right side of the bar": | |
ax.text(end + 0.5, i, f"{completion}%", | |
ha='left', va='center', fontsize=progress_text_size, fontname='Times New Roman') | |
# Add milestones if enabled | |
if show_milestones: | |
for i, (task_name, start, end, duration, completion) in enumerate(tasks): | |
if end % 3 == 0: # Just for demo - add milestones every 3 months | |
if milestone_style == "star": | |
ax.plot(end, i, marker="*", markersize=15, color=milestone_color) | |
elif milestone_style == "diamond": | |
ax.plot(end, i, marker="D", markersize=8, color=milestone_color) | |
elif milestone_style == "triangle": | |
ax.plot(end, i, marker="^", markersize=10, color=milestone_color) | |
else: | |
ax.plot(end, i, marker="o", markersize=8, color=milestone_color) | |
# Add today line if enabled | |
if show_today_line: | |
today_month = 6 # For demo purposes | |
ax.axvline(x=today_month, color='red', linestyle=today_line_style, | |
linewidth=2, alpha=0.7, label='Today') | |
ax.text(today_month, len(tasks)+0.2, 'Today', ha='center', | |
color='red', fontsize=font_properties['tick_size']) | |
ax.set_xlabel("Months", fontsize=font_properties['axis_label_size'], fontname='Times New Roman') | |
ax.set_ylabel("Tasks", fontsize=font_properties['axis_label_size'], fontname='Times New Roman') | |
ax.set_title("Project Timeline", fontsize=font_properties['title_size'], fontname='Times New Roman') | |
ax.set_xticks(range(1, 13)) | |
ax.set_xticklabels([datetime(2000, m, 1).strftime('%b') for m in range(1, 13)], | |
fontsize=font_properties['tick_size'], fontname='Times New Roman') | |
ax.set_yticks(range(len(tasks))) | |
ax.set_yticklabels([t[0] for t in tasks], fontsize=font_properties['tick_size'], fontname='Times New Roman') | |
# Add grid if enabled | |
if show_grid: | |
ax.grid(True, linestyle=grid_style, alpha=0.7) | |
# Add legend with selected position | |
if show_milestones or show_today_line: | |
ax.legend(loc=legend_loc_map[legend_position], fontsize=font_properties['legend_size'], frameon=True) | |
plt.tight_layout() | |
return fig | |
# Function to create a donut pie chart for overall project progress | |
def create_dual_donut_chart(total_duration, completed_duration, tasks, gap_size, font_properties, | |
border_line, show_inner_pie, show_outer_pie, pie_text_size, pie_text_orientation): | |
fig, ax = plt.subplots(figsize=(8, 8)) | |
# Prepare explode parameter for largest segment if enabled | |
explode = None | |
if explode_largest and show_outer_pie: | |
task_sizes = [task[1] * (task[2] / 100) if task[1] != 0 else 0.1 for task in tasks] | |
max_idx = task_sizes.index(max(task_sizes)) | |
explode = [0.1 if i == max_idx else 0 for i in range(len(tasks))] | |
# Configure 3D effect if enabled | |
if use_3d_pie: | |
ax = plt.subplot(111, projection='3d') | |
fig.subplots_adjust(left=0, right=1, bottom=0, top=1) | |
# Inner donut: Overall project progress (if visible) | |
if show_inner_pie: | |
colors = [current_theme[0], adjust_color_brightness(current_theme[0], 0.5)] | |
inner_wedges, inner_texts, inner_autotexts = ax.pie( | |
[completed_duration, total_duration - completed_duration], | |
labels=['Completed', 'Remaining'], | |
colors=colors, | |
autopct='%1.1f%%', startangle=90, | |
wedgeprops={'width': 0.4, 'edgecolor': border_line, 'linewidth': 2.0}, | |
shadow=use_3d_pie) | |
# Format inner pie text | |
for text in inner_texts + inner_autotexts: | |
text.set_fontsize(pie_text_size) | |
text.set_fontname('Times New Roman') | |
# Outer donut: Tasks performance (if visible) | |
if show_outer_pie: | |
task_sizes = [task[1] * (task[2] / 100) if task[1] != 0 else 0.1 for task in tasks] # Avoid zero sizes | |
task_sizes = [max(size, 0.1) for size in task_sizes] | |
task_labels = [task[0] for task in tasks] | |
colors = [current_theme[i % len(current_theme)] for i in range(len(tasks))] | |
outer_wedges, outer_texts, outer_autotexts = ax.pie( | |
task_sizes, labels=task_labels, autopct='%1.1f%%', startangle=90, radius=0.6 - gap_size, | |
colors=colors, | |
wedgeprops={'width': 0.4, 'edgecolor': border_line, 'linewidth': 2.0}, | |
explode=explode, shadow=use_3d_pie) | |
# Format outer pie text and apply orientation | |
for text in outer_texts + outer_autotexts: | |
text.set_fontsize(pie_text_size) | |
text.set_fontname('Times New Roman') | |
# Apply text orientation | |
if pie_text_orientation == "font print in vertical": | |
text.set_rotation(90) | |
elif pie_text_orientation == "font print in circular": | |
if text in outer_texts: # Only rotate the labels, not the percentages | |
angle = (text.get_position()[0] + text.get_position()[1]) * 180 | |
text.set_rotation(angle) | |
text.set_rotation_mode("anchor") | |
text.set_va("center") | |
if not use_3d_pie: | |
ax.axis('equal') # Keep the pie chart circular | |
ax.text(0, 0, "Project Progress", fontsize=font_properties['title_size'], | |
fontname='Times New Roman', ha='center') | |
plt.tight_layout() | |
return fig | |
# Function to create an interactive gantt chart with plotly | |
def create_gantt_chart(tasks): | |
df = [] | |
# Prepare data for gantt chart | |
for i, (name, start, end, completion, depends_on, risk_level) in enumerate(tasks): | |
# Set color based on risk level | |
if risk_highlighting: | |
if risk_level == "High": | |
color = "rgb(255, 0, 0)" # Red for high risk | |
elif risk_level == "Medium": | |
color = "rgb(255, 165, 0)" # Orange for medium risk | |
else: | |
color = "rgb(0, 128, 0)" # Green for low risk | |
else: | |
color = current_theme[i % len(current_theme)] | |
# Add main task bar | |
df.append(dict( | |
Task=name, | |
Start=start, | |
Finish=end, | |
Complete=f"{completion}%", | |
Resource=resource_tasks[i][1] if i < len(resource_tasks) else "", | |
Priority=resource_tasks[i][3] if i < len(resource_tasks) else "Medium", | |
Risk=risk_level, | |
Color=color | |
)) | |
# Create figure | |
fig = px.timeline( | |
df, | |
x_start="Start", | |
x_end="Finish", | |
y="Task", | |
color="Risk" if risk_highlighting else None, | |
color_discrete_map={ | |
"High": "red", | |
"Medium": "orange", | |
"Low": "green" | |
} if risk_highlighting else None, | |
hover_data=["Complete", "Resource", "Priority"] | |
) | |
# Add resource labels if enabled | |
if show_resource_labels: | |
for i, task in enumerate(df): | |
fig.add_annotation( | |
x=task["Start"] + (task["Finish"] - task["Start"])/2, | |
y=task["Task"], | |
text=task["Resource"], | |
showarrow=False, | |
font=dict(color="black", size=10) | |
) | |
# Add dependencies if enabled | |
if show_dependencies: | |
for i, (name, start, end, completion, depends_on, risk) in enumerate(tasks): | |
for dep in depends_on: | |
# Find the dependent task index | |
dep_idx = next((j for j, task in enumerate(tasks) if task[0] == dep), None) | |
if dep_idx is not None: | |
# Add an arrow from the end of dependent task to start of current task | |
dep_end = tasks[dep_idx][2] | |
fig.add_shape( | |
type="line", | |
x0=dep_end, | |
y0=dep_idx, | |
x1=start, | |
y1=i, | |
line=dict(color="grey", width=1, dash="dot"), | |
layer="below" | |
) | |
# Update layout | |
fig.update_layout( | |
title="Project Gantt Chart", | |
height=gantt_height, | |
xaxis_title="Timeline", | |
yaxis_title="Tasks", | |
font=dict(family="Times New Roman"), | |
showlegend=True if risk_highlighting else False | |
) | |
# Highlight weekends if needed | |
if weekend_color: | |
# This is simplified - in a real app, you'd calculate actual weekends | |
fig.update_xaxes( | |
rangebreaks=[dict(pattern="day of week", bounds=[6, 7])] | |
) | |
return fig | |
# Function to create resource allocation chart | |
def create_resource_allocation_chart(tasks): | |
if resource_chart_type == "Stacked Bar": | |
# Extract resources and their allocations | |
resources = {} | |
for task in tasks: | |
name, resource, allocation, priority, risk = task | |
if resource not in resources: | |
resources[resource] = [] | |
resources[resource].append((name, allocation, priority, risk)) | |
# Prepare data for plotting | |
labels = list(resources.keys()) | |
data = [] | |
colors = [] | |
for i, resource in enumerate(labels): | |
resource_tasks = resources[resource] | |
total_allocation = sum(allocation for _, allocation, _, _ in resource_tasks) | |
data.append(total_allocation) | |
# Color based on overallocation | |
if show_overallocation and total_allocation > overallocation_threshold: | |
colors.append("red") | |
else: | |
colors.append(current_theme[i % len(current_theme)]) | |
# Create figure | |
fig, ax = plt.subplots(figsize=(10, 6)) | |
ax.bar(labels, data, color=colors) | |
# Add threshold line if overallocation checking is enabled | |
if show_overallocation: | |
ax.axhline(y=overallocation_threshold, color='red', linestyle='--', label=f"Threshold ({overallocation_threshold}%)") | |
# Highlight overallocated resources | |
if show_overallocation: | |
for i, allocation in enumerate(data): | |
if allocation > overallocation_threshold: | |
ax.annotate("Overallocated", | |
xy=(i, allocation), | |
xytext=(0, 10), | |
textcoords="offset points", | |
ha='center', |