-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
123 lines (104 loc) · 3.82 KB
/
app.py
File metadata and controls
123 lines (104 loc) · 3.82 KB
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import logging
import matplotlib.pyplot as plt
import numpy as np
import streamlit as st
from include.helpers import (
calc_daily_engineer_cost,
get_engineers,
get_work_items,
run_simulations,
)
# Configure logging
logging.basicConfig(
level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s"
)
st.title("Monte Carlo Delivery Estimator")
st.text(
"This estimator uses Monte Carlo simulations to estimate delivery effort and cost."
)
with st.sidebar:
if st.button("Reset Form"):
st.session_state.clear() # Clears all session state variables
st.rerun()
st.subheader("Team Allocation")
try:
engineers = get_engineers()
for engineer in engineers:
engineer["count"] = st.sidebar.slider(
f"{engineer['level']}",
min_value=0,
max_value=10,
value=engineer["count"],
)
except Exception as e:
logging.error(f"Error retrieving engineers: {e}")
st.error("Error retrieving engineers. Please check the logs for more details.")
st.divider()
st.subheader("Simulation Settings")
num_simulations = st.sidebar.number_input(
"Number of Simulations", min_value=1000, max_value=50000, value=10000
)
st.divider()
with st.expander("Work Items in Scope", expanded=True):
try:
work_items = get_work_items()
col1, col2 = st.columns(2)
midpoint = (len(work_items) + 1) // 2
with col1:
for item in work_items[:midpoint]:
item["count"] = st.number_input(
label=item["name"], min_value=0, max_value=30, value=0
)
with col2:
for item in work_items[midpoint:]:
item["count"] = st.number_input(
label=item["name"], min_value=0, max_value=30, value=0
)
except Exception as e:
logging.error(f"Error retrieving work items: {e}")
st.error("Error retrieving work items. Please check the logs for more details.")
if st.button("Run Simulation"):
try:
with st.container():
simulation_results = run_simulations(num_simulations, work_items, engineers)
daily_cost = calc_daily_engineer_cost(engineers)
p50 = np.percentile(simulation_results, 50)
p80 = np.percentile(simulation_results, 80)
p95 = np.percentile(simulation_results, 95)
cost_p50 = p50 * daily_cost
cost_p80 = p80 * daily_cost
cost_p95 = p95 * daily_cost
st.session_state.expand_inputs = False
st.session_state.simulation_run = True
st.subheader("Simulation Results")
st.write(f"P50 Estimate: {p50:.2f} days (Cost: ${cost_p50:,.2f})")
st.write(f"P80 Estimate: {p80:.2f} days (Cost: ${cost_p80:,.2f})")
st.write(f"P95 Estimate: {p95:.2f} days (Cost: ${cost_p95:,.2f})")
fig, ax = plt.subplots()
ax.hist(simulation_results, bins=50, edgecolor="black", alpha=0.7)
ax.axvline(
p50,
color="blue",
linestyle="dashed",
linewidth=2,
label=f"P50: {p50:.2f}",
)
ax.axvline(
p80,
color="orange",
linestyle="dashed",
linewidth=2,
label=f"P80: {p80:.2f}",
)
ax.axvline(
p95,
color="red",
linestyle="dashed",
linewidth=2,
label=f"P95: {p95:.2f}",
)
ax.legend()
st.pyplot(fig)
except Exception as e:
logging.error(f"Error running simulation: {e}")
st.error("Error running simulation. Please check the logs for more details.")