Skip to content

Commit 115eda8

Browse files
authored
calendar scheduling with interactive slider and dead hours
Refactor task_calendar_scheduler.py to include a slider for adjusting event times and improve plotting functionality with resource-based color mapping.
1 parent aef7ee9 commit 115eda8

File tree

1 file changed

+100
-46
lines changed

1 file changed

+100
-46
lines changed

src/eigenp_utils/task_calendar_scheduler.py

Lines changed: 100 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
import marimo
22

3-
__generated_with = "0.11.18"
3+
__generated_with = "unknown"
44
app = marimo.App(width="medium")
55

66

7+
@app.cell
8+
def _(mo):
9+
time_hrs_slider_ = mo.ui.slider(start=-10, stop=10, step = 0.5)
10+
time_hrs_slider_
11+
return (time_hrs_slider_,)
12+
13+
714
@app.cell(hide_code=True)
815
def _(cal, plot_calendar):
916

1017
# Plot calendar
1118
fig = plot_calendar(cal)
12-
return (fig,)
19+
return
1320

1421

1522
@app.cell
@@ -27,7 +34,7 @@ def _():
2734
@app.cell
2835
def _(pd):
2936
from dataclasses import dataclass, field
30-
from datetime import datetime, timedelta
37+
from datetime import datetime, timedelta, time
3138
from typing import Optional, Union, List
3239
import uuid
3340
import copy
@@ -105,51 +112,74 @@ def update_event(self,
105112
for e in targets:
106113
e.start = e.start + timedelta(seconds=delta_start)
107114
e.end = e.end + timedelta(seconds=delta_end)
108-
109-
return (
110-
Calendar,
111-
Event,
112-
List,
113-
Optional,
114-
Union,
115-
copy,
116-
dataclass,
117-
datetime,
118-
events_to_dataframe,
119-
field,
120-
timedelta,
121-
uuid,
122-
)
115+
return Calendar, Event, datetime, events_to_dataframe, time, timedelta
123116

124117

125118
@app.cell
126119
def _(Calendar, events_to_dataframe):
127120
import plotly.express as px
128121

129122
def plot_calendar(calendar: Calendar, show: bool = True):
123+
"""
124+
Plots the calendar events, attempting to use resource names as colors.
125+
Falls back to standard coloring if resource names are not valid colors.
126+
"""
130127
df = events_to_dataframe(calendar.events)
131-
fig = px.timeline(
132-
df,
133-
x_start="start",
134-
x_end="end",
135-
y="group", # Grouped lanes on y-axis
136-
color="resource", # Optional color-coding
137-
hover_data=["name", "description"]
138-
)
139-
fig.update_yaxes(autorange="reversed") # Keep top-down order :contentReference[oaicite:6]{index=6}
140-
# fig.update_xaxes(
141-
# tickformat="%H:%M", # Hour:Minute format
142-
# dtick=3600000 # One-hour intervals (ms) :contentReference[oaicite:7]{index=7}
143-
# )
128+
129+
try:
130+
# --- Attempt 1: Use resource names as colors ---
131+
132+
# 1. Create the color map from unique resource names
133+
# This assumes resource names are valid color strings (e.g., "Red", "black", "blue")
134+
unique_resources = df['resource'].unique()
135+
color_map = {res: res for res in unique_resources}
136+
137+
print("Attempting to apply color palette from resource names...")
138+
139+
# 2. Try to plot using this map for the 'color_discrete_map' argument
140+
# This line will fail if any name in 'color_map' is not a valid color.
141+
fig = px.timeline(
142+
df,
143+
x_start="start",
144+
x_end="end",
145+
y="group",
146+
color="resource",
147+
color_discrete_map=color_map, # <-- This is the custom palette
148+
hover_data=["name", "description"]
149+
)
150+
print("Successfully applied custom color palette.")
151+
152+
except Exception as e:
153+
# --- Attempt 2: Fallback to standard coloring ---
154+
155+
print(f"\n--- PLOTTING WARNING ---")
156+
print(f"Failed to apply custom palette from resource names. Error: {e}")
157+
print("This likely means a 'resource' name isn't a valid color (e.g., 'Incubator1' instead of 'red').")
158+
print("Falling back to standard Plotly coloring.\n")
159+
160+
# Plot again, but without the 'color_discrete_map' argument
161+
fig = px.timeline(
162+
df,
163+
x_start="start",
164+
x_end="end",
165+
y="group",
166+
color="resource", # <-- Standard coloring
167+
hover_data=["name", "description"]
168+
)
169+
170+
# --- Common layout updates ---
171+
fig.update_yaxes(autorange="reversed")
144172
fig.update_layout(title="Linked Event Schedule", xaxis_title="Time", yaxis_title="Group")
173+
145174
if show:
146175
fig.show()
176+
147177
return fig
148-
return plot_calendar, px
178+
return (plot_calendar,)
149179

150180

151181
@app.cell
152-
def _(Calendar, Event, datetime, timedelta):
182+
def _(Calendar, Event, datetime, time, time_hrs_slider_, timedelta):
153183
# events = [
154184
# Event("1", "Opening Ceremony", datetime(2025,5,12,9,0), datetime(2025,5,12,10,0), resource="Main Hall"),
155185
# Event("2", "Keynote", datetime(2025,5,12,10,30),datetime(2025,5,18,11,30),resource="Main Hall"),
@@ -162,28 +192,52 @@ def _(Calendar, Event, datetime, timedelta):
162192

163193
# Initial events with a shared linkGroup "Team"
164194

165-
dissection_time = datetime(2025,6,5,9,0)
195+
dissection_time_ = datetime(2025,10,30,8,0)
196+
197+
dissection_time = dissection_time_ - timedelta(hours=time_hrs_slider_.value)
198+
166199

167200
events = [
168-
# HH 10
169-
Event("Incub to hh8", dissection_time - timedelta(hours=9) - timedelta(hours=2) - timedelta(hours=30), timedelta(hours=30), group="HH10", resource="Blue"),
170-
Event("Harvest & Epor", dissection_time - timedelta(hours=9) - timedelta(hours=2), timedelta(hours=2), group="HH10", resource="Red", linkGroup="HH10"),
171-
Event("Epor Incub", dissection_time - timedelta(hours=9), timedelta(hours=9), group="HH10", resource="Blue", linkGroup="HH10"),
172-
# HH 15
173-
Event("Incub to hh11", dissection_time - timedelta(hours=17) - timedelta(hours=2) - timedelta(hours=42), timedelta(hours=42), group="HH15", resource="Blue", linkGroup="HH15"),
174-
Event("Harvest & Epor", dissection_time - timedelta(hours=17) - timedelta(hours=2), timedelta(hours=2), group="HH15", resource="Red", linkGroup="HH15"),
175-
Event("Epor Incub", dissection_time - timedelta(hours=17), timedelta(hours=17), group="HH15", resource="Blue", linkGroup="HH15"),
201+
# # HH 10
202+
# Event("Incub to hh8", dissection_time - timedelta(hours=9) - timedelta(hours=2) - timedelta(hours=30), timedelta(hours=30), group="HH10", resource="Blue"),
203+
# Event("Harvest & Epor", dissection_time - timedelta(hours=9) - timedelta(hours=2), timedelta(hours=2), group="HH10", resource="Red", linkGroup="HH10"),
204+
# Event("Epor Incub", dissection_time - timedelta(hours=9), timedelta(hours=9), group="HH10", resource="Blue", linkGroup="HH10"),
205+
# # HH 15
206+
# Event("Incub to hh11", dissection_time - timedelta(hours=17) - timedelta(hours=2) - timedelta(hours=42), timedelta(hours=42), group="HH15", resource="Blue", linkGroup="HH15"),
207+
# Event("Harvest & Epor", dissection_time - timedelta(hours=17) - timedelta(hours=2), timedelta(hours=2), group="HH15", resource="Red", linkGroup="HH15"),
208+
# Event("Epor Incub", dissection_time - timedelta(hours=17), timedelta(hours=17), group="HH15", resource="Blue", linkGroup="HH15"),
176209
# HH 18
177210
Event("Incub to hh11", dissection_time - timedelta(hours=34) - timedelta(hours=2) - timedelta(hours=42), timedelta(hours=42), group="HH18", resource="Blue", linkGroup="HH18"),
178211
Event("Harvest & Epor", dissection_time - timedelta(hours=34) - timedelta(hours=2), timedelta(hours=2), group="HH18", resource="Red", linkGroup="HH18"),
179212
Event("Epor Incub", dissection_time - timedelta(hours=34), timedelta(hours=34),group="HH18", resource="Blue", linkGroup="HH18"),
180213
# Dissect & Dissociate
181-
Event("Dissect", dissection_time, timedelta(minutes=45) ,group="HH10", resource="Red"),
182-
Event("Dissociate", datetime(2025,6,5,10,30), timedelta(hours=1.5), group="HH10", resource="Red"),
214+
Event("Dissect", dissection_time, timedelta(minutes=45) ,group="HH18", resource="Red"),
215+
# Event("Dissociate", datetime(2025,6,5,10,30), timedelta(hours=1.5), group="HH10", resource="Red"),
216+
217+
]
218+
219+
220+
### Nights - Dead hours to avoid (Generated)
221+
# We define the start time (10 PM) and duration (8 hours)
222+
night_start_time = time(22, 0)
223+
night_duration = timedelta(hours=8)
224+
225+
# List comprehension to generate dead hours for 4 nights leading up to dissection
226+
# range(1, 5) will loop for i = 1, 2, 3, 4
227+
dead_hours = [
228+
Event(
229+
"Night", # Event name
230+
datetime.combine(dissection_time_.date() - timedelta(days=i), night_start_time), # Start time: 10 PM on the day i days before
231+
night_duration, # Duration: 8 hours (10 PM to 6 AM)
232+
group="Dead",
233+
resource="Black",
234+
linkGroup="Dead"
235+
)
236+
for i in range(1, 5) # Generates for 1, 2, 3, and 4 days prior
183237
]
184238

185-
cal = Calendar(events)
186-
return cal, dissection_time, events
239+
cal = Calendar(events + dead_hours)
240+
return (cal,)
187241

188242

189243
@app.cell

0 commit comments

Comments
 (0)