Spaces:
Sleeping
Update app.py
Browse filesimport streamlit as st
import pandas as pd
import networkx as nx
st.title("๐
Project Scheduler & Critical Path (Days Based)")
project_type = st.selectbox("Select Project Type", ["Building", "Bridge", "Road", "Other"])
st.markdown("### ๐ Task Details")
st.markdown("""
Each row: `TaskID, Duration (in days), Dependency, Relationship Type (FS/SS/FF/SF), Lag (optional)`
- Example: `A,5,,,`\n
- Example: `B,3,A,FS,0`\n
- Example: `C,2,A,SS,1`
""")
task_data = st.text_area("Paste your task data here:")
if st.button("๐งฎ Calculate Critical Path"):
tasks = {}
dependencies = []
# Parse input data
for line in task_data.strip().split("\n"):
parts = [p.strip() for p in line.split(",")]
task_id, duration = parts[0], int(parts[1])
dep = parts[2] if len(parts) > 2 and parts[2] else None
rel_type = parts[3] if len(parts) > 3 and parts[3] else "FS"
lag = int(parts[4]) if len(parts) > 4 and parts[4] else 0
tasks[task_id] = duration
if dep:
dependencies.append((dep, task_id, rel_type.upper(), lag))
# Build directed graph
G = nx.DiGraph()
for task_id in tasks:
G.add_node(task_id, duration=tasks[task_id])
for from_task, to_task, rel, lag in dependencies:
G.add_edge(from_task, to_task, type=rel, lag=lag)
# Initialize earliest and latest start/finish times
es = {} # Earliest start times
ef = {} # Earliest finish times
ls = {} # Latest start times
lf = {} # Latest finish times
# Topological order to ensure we process tasks in correct order
top_order = list(nx.topological_sort(G))
# Forward pass to calculate ES and EF
for task in top_order:
preds = list(G.predecessors(task))
if not preds:
es[task] = 0
else:
max_start = 0
for pred in preds:
edge = G.edges[pred, task]
pred_type = edge.get("type", "FS")
lag = edge.get("lag", 0)
if pred_type == "FS":
start_time = ef[pred] + lag
elif pred_type == "SS":
start_time = es[pred] + lag
elif pred_type == "FF":
start_time = ef[pred] - tasks[task] + lag
elif pred_type == "SF":
start_time = es[pred] - tasks[task] + lag
max_start = max(max_start, start_time)
es[task] = max_start
ef[task] = es[task] + tasks[task]
# Backward pass to calculate LS and LF
for task in reversed(top_order):
succs = list(G.successors(task))
if not succs:
lf[task] = ef[task]
else:
min_finish = min(ls[succ] for succ in succs)
lf[task] = min_finish
ls[task] = lf[task] - tasks[task]
# Critical path calculation: Task is critical if ES == LS
critical_path = [t for t in top_order if es[t] == ls[t]]
st.success(f"Critical Path: {' โ '.join(critical_path)}")
st.info(f"Total Project Duration: {max(ef.values())} days")
|
@@ -1,112 +1,4 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
import pandas as pd
|
| 3 |
-
import networkx as nx
|
| 4 |
-
import plotly.figure_factory as ff
|
| 5 |
|
| 6 |
-
st.title("๐
Project Scheduler & Critical Path (Days Based)")
|
| 7 |
-
|
| 8 |
-
project_type = st.selectbox("Select Project Type", ["Building", "Bridge", "Road", "Other"])
|
| 9 |
-
|
| 10 |
-
st.markdown("### ๐ Task Details")
|
| 11 |
-
st.markdown("""
|
| 12 |
-
Each row: `TaskID, Duration (in days), Dependency, Relationship Type (FS/SS/FF/SF), Lag (optional)`
|
| 13 |
-
- Example: `A,5,,,`\n
|
| 14 |
-
- Example: `B,3,A,FS,0`\n
|
| 15 |
-
- Example: `C,2,A,SS,1`
|
| 16 |
-
""")
|
| 17 |
-
|
| 18 |
-
task_data = st.text_area("Paste your task data here:")
|
| 19 |
-
|
| 20 |
-
if st.button("๐งฎ Calculate Critical Path"):
|
| 21 |
-
tasks = {}
|
| 22 |
-
dependencies = []
|
| 23 |
-
|
| 24 |
-
for line in task_data.strip().split("\n"):
|
| 25 |
-
parts = [p.strip() for p in line.split(",")]
|
| 26 |
-
task_id, duration = parts[0], int(parts[1])
|
| 27 |
-
dep = parts[2] if len(parts) > 2 and parts[2] else None
|
| 28 |
-
rel_type = parts[3] if len(parts) > 3 and parts[3] else "FS"
|
| 29 |
-
lag = int(parts[4]) if len(parts) > 4 and parts[4] else 0
|
| 30 |
-
tasks[task_id] = duration
|
| 31 |
-
if dep:
|
| 32 |
-
dependencies.append((dep, task_id, rel_type.upper(), lag))
|
| 33 |
-
|
| 34 |
-
# Build graph
|
| 35 |
-
G = nx.DiGraph()
|
| 36 |
-
for task_id in tasks:
|
| 37 |
-
G.add_node(task_id, duration=tasks[task_id])
|
| 38 |
-
for from_task, to_task, rel, lag in dependencies:
|
| 39 |
-
G.add_edge(from_task, to_task, type=rel, lag=lag)
|
| 40 |
-
|
| 41 |
-
# CPM calculation (simplified for FS and SS, other types handled properly now)
|
| 42 |
-
es = {}
|
| 43 |
-
ef = {}
|
| 44 |
-
top_order = list(nx.topological_sort(G))
|
| 45 |
-
|
| 46 |
-
for task in top_order:
|
| 47 |
-
preds = list(G.predecessors(task))
|
| 48 |
-
if not preds:
|
| 49 |
-
es[task] = 0
|
| 50 |
-
else:
|
| 51 |
-
max_start = 0
|
| 52 |
-
for pred in preds:
|
| 53 |
-
edge = G.edges[pred, task]
|
| 54 |
-
pred_type = edge.get("type", "FS")
|
| 55 |
-
lag = edge.get("lag", 0)
|
| 56 |
-
pred_duration = G.nodes[pred]["duration"]
|
| 57 |
-
if pred_type == "FS":
|
| 58 |
-
start_time = ef[pred] + lag
|
| 59 |
-
elif pred_type == "SS":
|
| 60 |
-
start_time = es[pred] + lag
|
| 61 |
-
elif pred_type == "FF":
|
| 62 |
-
start_time = ef[pred] - tasks[task] + lag
|
| 63 |
-
elif pred_type == "SF":
|
| 64 |
-
start_time = es[pred] - tasks[task] + lag
|
| 65 |
-
else:
|
| 66 |
-
start_time = ef[pred] + lag
|
| 67 |
-
max_start = max(max_start, start_time)
|
| 68 |
-
es[task] = max_start
|
| 69 |
-
ef[task] = es[task] + tasks[task]
|
| 70 |
-
|
| 71 |
-
# Backward pass
|
| 72 |
-
lf = {}
|
| 73 |
-
ls = {}
|
| 74 |
-
for task in reversed(top_order):
|
| 75 |
-
succs = list(G.successors(task))
|
| 76 |
-
if not succs:
|
| 77 |
-
lf[task] = ef[task]
|
| 78 |
-
else:
|
| 79 |
-
min_finish = min(ls[succ] for succ in succs)
|
| 80 |
-
lf[task] = min_finish
|
| 81 |
-
ls[task] = lf[task] - tasks[task]
|
| 82 |
-
|
| 83 |
-
critical_path = [t for t in top_order if es[t] == ls[t]]
|
| 84 |
-
|
| 85 |
-
st.success(f"Critical Path: {' โ '.join(critical_path)}")
|
| 86 |
-
st.info(f"Total Project Duration: {max(ef.values())} days")
|
| 87 |
-
|
| 88 |
-
# Gantt chart (adjusted to handle display properly)
|
| 89 |
-
df = pd.DataFrame([
|
| 90 |
-
{
|
| 91 |
-
"Task": t,
|
| 92 |
-
"Start": f"Day {es[t]}",
|
| 93 |
-
"Finish": f"Day {ef[t]}",
|
| 94 |
-
"Start_day": es[t],
|
| 95 |
-
"Finish_day": ef[t],
|
| 96 |
-
"Resource": "Critical" if t in critical_path else "Non-Critical"
|
| 97 |
-
}
|
| 98 |
-
for t in top_order
|
| 99 |
-
])
|
| 100 |
-
|
| 101 |
-
fig = ff.create_gantt(
|
| 102 |
-
df,
|
| 103 |
-
index_col="Resource",
|
| 104 |
-
show_colorbar=True,
|
| 105 |
-
group_tasks=True,
|
| 106 |
-
showgrid_x=True,
|
| 107 |
-
showgrid_y=True
|
| 108 |
-
)
|
| 109 |
-
st.plotly_chart(fig, use_container_width=True)
|
| 110 |
|
| 111 |
|
| 112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
|
| 4 |
|