ModelKiln/calendar-workload-demo
0
1import joblib2import numpy as np3import gradio as gr4 5# Lataa malli6model = joblib.load("workload_model.joblib")7 8CLASS_NAMES = {0: "Low", 1: "Medium", 2: "High"}9EMOJI = {"Low": "🟢", "Medium": "🟠", "High": "🔴"}10 11# Esimerkkipäivät dropdownille12EXAMPLE_PRESETS = {13 "Balanced day": [3, 2.5, 4, 2, 45, 9, 16],14 "Overloaded day": [9, 7.5, 15, 0, 10, 9, 18],15 "Focused day": [2, 1.5, 2, 3, 60, 8, 15],16}17 18 19def predict_workload(20 meetings_count,21 total_meeting_hours,22 context_switches,23 deep_work_blocks,24 break_minutes,25 day_start_hour,26 day_end_hour,27):28 X = np.array([[29 meetings_count,30 total_meeting_hours,31 context_switches,32 deep_work_blocks,33 break_minutes,34 day_start_hour,35 day_end_hour,36 ]])37 38 probs = model.predict_proba(X)[0]39 pred_class = int(np.argmax(probs))40 pred_label = CLASS_NAMES[pred_class]41 confidence = float(probs[pred_class])42 43 probs_dict = {name: float(probs[i]) for i, name in CLASS_NAMES.items()}44 45 headline = (46 f"<div style='text-align:center; font-size:1.8rem; margin:1rem 0;'>"47 f"{EMOJI[pred_label]} <strong>{pred_label} Workload</strong>"48 f"</div>"49 f"<div style='text-align:center; color:#555; font-size:1.1rem;'>"50 f"Confidence: <strong>{confidence * 100:.1f}%</strong>"51 f"</div>"52 )53 54 # --- practical tips ---55 tips = []56 day_length = day_end_hour - day_start_hour57 58 if meetings_count >= 8 or total_meeting_hours >= 6:59 tips.append(60 "Consider blocking <strong>meeting-free focus time</strong> (e.g. 2–3h) and moving "61 "non-essential meetings to another day."62 )63 if context_switches >= 12:64 tips.append(65 "Try to <strong>batch similar tasks or meetings</strong> together to reduce context switching."66 )67 if deep_work_blocks == 0:68 tips.append(69 "Add at least <strong>one deep work block (60–90 minutes)</strong> for focused work without notifications."70 )71 if break_minutes < 30 and day_length >= 8:72 tips.append(73 "Increase <strong>break time</strong> slightly – even short 5–10 minute breaks every few hours reduce fatigue."74 )75 if day_length > 9:76 tips.append(77 "Your workday is long – consider <strong>moving low-priority tasks</strong> to another day or finishing earlier."78 )79 80 if not tips:81 tips.append(82 "Your setup looks balanced! To stay sustainable, consider scheduling regular deep work and micro-breaks."83 )84 85 tips_html = "<ul style='padding-left:1.2rem; line-height:1.6;'>"86 for tip in tips:87 tips_html += f"<li>{tip}</li>"88 tips_html += "</ul>"89 90 explanation = (91 "<h3 style='margin-top:1.5rem;'>💡 Personalized Suggestions</h3>"92 + tips_html +93 "<h3 style='margin-top:1.5rem;'>🧠 How the Model Works</h3>"94 "<p style='color:#555;'>This AI estimates your perceived workload using:</p>"95 "<ul style='padding-left:1.2rem; color:#555;'>"96 "<li>Number and duration of meetings</li>"97 "<li>Frequency of task/meeting switches (context switches)</li>"98 "<li>Presence of uninterrupted deep work blocks</li>"99 "<li>Total break time during the day</li>"100 "<li>Overall workday length</li>"101 "</ul>"102 )103 104 return headline, probs_dict, explanation105 106 107def load_example(example_name):108 """Täyttää sliderit valitun esimerkin arvoilla."""109 if example_name in EXAMPLE_PRESETS:110 return EXAMPLE_PRESETS[example_name]111 return [4, 3, 6, 1, 30, 9, 17]112 113 114# Teema115theme = gr.themes.Soft(116 primary_hue=gr.themes.colors.orange,117 secondary_hue=gr.themes.colors.rose,118 neutral_hue=gr.themes.colors.slate,119 radius_size=gr.themes.sizes.radius_md,120).set(121 button_primary_background_fill="*primary_500",122 button_primary_background_fill_hover="*primary_600",123 block_title_text_weight="600",124)125 126# CSS127css = """128#app-container {129 max-width: 1200px;130 margin: 0 auto;131 padding: 0 1.5rem;132}133#app-header {134 text-align: center;135 margin-bottom: 2rem;136}137#app-header h1 {138 font-weight: 700;139 font-size: 2.2rem;140 color: #222;141 margin-bottom: 0.4rem;142}143#app-header p {144 font-size: 1.1rem;145 color: #666;146 max-width: 650px;147 margin: 0 auto;148 line-height: 1.5;149}150.gr-button-primary {151 font-weight: 600;152 padding: 0.6rem 1.4rem;153 font-size: 1.05rem;154}155@media (max-width: 768px) {156 #app-header h1 {157 font-size: 1.8rem;158 }159 #app-header p {160 font-size: 1rem;161 }162 .gr-button-primary {163 width: 100%;164 padding: 0.7rem;165 }166}167"""168 169with gr.Blocks(theme=theme, css=css, title="🗓️ Workload Estimator") as demo:170 with gr.Column(elem_id="app-container"):171 gr.Markdown(172 """173 <div id="app-header">174 <h1>🗓️ Calendar Workload Estimator</h1>175 <p>Estimate your daily cognitive load based on meetings, focus time, and recovery.</p>176 </div>177 """,178 elem_id="header"179 )180 181 with gr.Tab("📊 Estimate Your Day"):182 with gr.Row(equal_height=False):183 184 # VASEN SARake: dropdown + sliderit + nappi185 with gr.Column(scale=2):186 example_dropdown = gr.Dropdown(187 label="💡 Load example schedule",188 choices=list(EXAMPLE_PRESETS.keys()),189 value=None,190 interactive=True,191 )192 193 gr.Markdown("### 🗓️ Workday Structure")194 meetings_count = gr.Slider(0, 12, value=4, step=1, label="Meetings (count)")195 total_meeting_hours = gr.Slider(0, 9, value=3, step=0.5, label="Total meeting hours")196 context_switches = gr.Slider(0, 20, value=6, step=1, label="Context switches")197 198 gr.Markdown("### 🧘 Focus & Recovery")199 deep_work_blocks = gr.Slider(0, 4, value=1, step=1, label="Deep work blocks (≥60 min)")200 break_minutes = gr.Slider(0, 120, value=30, step=5, label="Total break minutes")201 202 gr.Markdown("### ⏰ Workday Timing")203 day_start_hour = gr.Slider(6, 11, value=9, step=1, label="Start hour (24h)")204 day_end_hour = gr.Slider(14, 21, value=17, step=1, label="End hour (24h)")205 206 btn = gr.Button("🔍 Analyze Workload", variant="primary")207 208 # OIKEA sarake: tulos209 with gr.Column(scale=1):210 gr.Markdown("### 📈 Result")211 headline_out = gr.HTML()212 probs_out = gr.Label(label="Workload Probabilities")213 explanation_out = gr.HTML()214 215 # Kun valitaan esimerkki → täytetään sliderit216 example_dropdown.change(217 load_example,218 inputs=example_dropdown,219 outputs=[220 meetings_count,221 total_meeting_hours,222 context_switches,223 deep_work_blocks,224 break_minutes,225 day_start_hour,226 day_end_hour,227 ],228 )229 230 # Varsinainen ennustekutsu231 btn.click(232 predict_workload,233 inputs=[234 meetings_count,235 total_meeting_hours,236 context_switches,237 deep_work_blocks,238 break_minutes,239 day_start_hour,240 day_end_hour,241 ],242 outputs=[headline_out, probs_out, explanation_out],243 )244 245 with gr.Tab("ℹ️ About"):246 gr.Markdown("""247 ### About This Tool248 249 This demo shows how **calendar metadata** can be used to estimate cognitive workload — helping you reflect on sustainability, focus, and recovery.250 251 - **Synthetic data only**: No real user data was used.252 - **Model**: Trained `RandomForestClassifier` (scikit-learn).253 - **Output**: 3-class workload (`Low`, `Medium`, `High`).254 - **Goal**: Spark reflection, not replace judgment.255 256 Use this to **simulate "what-if" scenarios**: 257 _"What if I cancel two meetings?"_ or _"What if I add a 90-min focus block?"_258 259 Built with **Python**, **scikit-learn** and **Gradio**, deployed on **Hugging Face Spaces**.260 """)261 262if __name__ == "__main__":263 demo.launch()264 265 266 267 268 