Spaces:
Sleeping
Sleeping
| import joblib | |
| import numpy as np | |
| import gradio as gr | |
| # Lataa malli | |
| model = joblib.load("workload_model.joblib") | |
| CLASS_NAMES = {0: "Low", 1: "Medium", 2: "High"} | |
| EMOJI = {"Low": "🟢", "Medium": "🟠", "High": "🔴"} | |
| # Esimerkkipäivät dropdownille | |
| EXAMPLE_PRESETS = { | |
| "Balanced day": [3, 2.5, 4, 2, 45, 9, 16], | |
| "Overloaded day": [9, 7.5, 15, 0, 10, 9, 18], | |
| "Focused day": [2, 1.5, 2, 3, 60, 8, 15], | |
| } | |
| def predict_workload( | |
| meetings_count, | |
| total_meeting_hours, | |
| context_switches, | |
| deep_work_blocks, | |
| break_minutes, | |
| day_start_hour, | |
| day_end_hour, | |
| ): | |
| X = np.array([[ | |
| meetings_count, | |
| total_meeting_hours, | |
| context_switches, | |
| deep_work_blocks, | |
| break_minutes, | |
| day_start_hour, | |
| day_end_hour, | |
| ]]) | |
| probs = model.predict_proba(X)[0] | |
| pred_class = int(np.argmax(probs)) | |
| pred_label = CLASS_NAMES[pred_class] | |
| confidence = float(probs[pred_class]) | |
| probs_dict = {name: float(probs[i]) for i, name in CLASS_NAMES.items()} | |
| headline = ( | |
| f"<div style='text-align:center; font-size:1.8rem; margin:1rem 0;'>" | |
| f"{EMOJI[pred_label]} <strong>{pred_label} Workload</strong>" | |
| f"</div>" | |
| f"<div style='text-align:center; color:#555; font-size:1.1rem;'>" | |
| f"Confidence: <strong>{confidence * 100:.1f}%</strong>" | |
| f"</div>" | |
| ) | |
| # --- practical tips --- | |
| tips = [] | |
| day_length = day_end_hour - day_start_hour | |
| if meetings_count >= 8 or total_meeting_hours >= 6: | |
| tips.append( | |
| "Consider blocking <strong>meeting-free focus time</strong> (e.g. 2–3h) and moving " | |
| "non-essential meetings to another day." | |
| ) | |
| if context_switches >= 12: | |
| tips.append( | |
| "Try to <strong>batch similar tasks or meetings</strong> together to reduce context switching." | |
| ) | |
| if deep_work_blocks == 0: | |
| tips.append( | |
| "Add at least <strong>one deep work block (60–90 minutes)</strong> for focused work without notifications." | |
| ) | |
| if break_minutes < 30 and day_length >= 8: | |
| tips.append( | |
| "Increase <strong>break time</strong> slightly – even short 5–10 minute breaks every few hours reduce fatigue." | |
| ) | |
| if day_length > 9: | |
| tips.append( | |
| "Your workday is long – consider <strong>moving low-priority tasks</strong> to another day or finishing earlier." | |
| ) | |
| if not tips: | |
| tips.append( | |
| "Your setup looks balanced! To stay sustainable, consider scheduling regular deep work and micro-breaks." | |
| ) | |
| tips_html = "<ul style='padding-left:1.2rem; line-height:1.6;'>" | |
| for tip in tips: | |
| tips_html += f"<li>{tip}</li>" | |
| tips_html += "</ul>" | |
| explanation = ( | |
| "<h3 style='margin-top:1.5rem;'>💡 Personalized Suggestions</h3>" | |
| + tips_html + | |
| "<h3 style='margin-top:1.5rem;'>🧠 How the Model Works</h3>" | |
| "<p style='color:#555;'>This AI estimates your perceived workload using:</p>" | |
| "<ul style='padding-left:1.2rem; color:#555;'>" | |
| "<li>Number and duration of meetings</li>" | |
| "<li>Frequency of task/meeting switches (context switches)</li>" | |
| "<li>Presence of uninterrupted deep work blocks</li>" | |
| "<li>Total break time during the day</li>" | |
| "<li>Overall workday length</li>" | |
| "</ul>" | |
| ) | |
| return headline, probs_dict, explanation | |
| def load_example(example_name): | |
| """Täyttää sliderit valitun esimerkin arvoilla.""" | |
| if example_name in EXAMPLE_PRESETS: | |
| return EXAMPLE_PRESETS[example_name] | |
| return [4, 3, 6, 1, 30, 9, 17] | |
| # Teema | |
| theme = gr.themes.Soft( | |
| primary_hue=gr.themes.colors.orange, | |
| secondary_hue=gr.themes.colors.rose, | |
| neutral_hue=gr.themes.colors.slate, | |
| radius_size=gr.themes.sizes.radius_md, | |
| ).set( | |
| button_primary_background_fill="*primary_500", | |
| button_primary_background_fill_hover="*primary_600", | |
| block_title_text_weight="600", | |
| ) | |
| # CSS | |
| css = """ | |
| #app-container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 0 1.5rem; | |
| } | |
| #app-header { | |
| text-align: center; | |
| margin-bottom: 2rem; | |
| } | |
| #app-header h1 { | |
| font-weight: 700; | |
| font-size: 2.2rem; | |
| color: #222; | |
| margin-bottom: 0.4rem; | |
| } | |
| #app-header p { | |
| font-size: 1.1rem; | |
| color: #666; | |
| max-width: 650px; | |
| margin: 0 auto; | |
| line-height: 1.5; | |
| } | |
| .gr-button-primary { | |
| font-weight: 600; | |
| padding: 0.6rem 1.4rem; | |
| font-size: 1.05rem; | |
| } | |
| @media (max-width: 768px) { | |
| #app-header h1 { | |
| font-size: 1.8rem; | |
| } | |
| #app-header p { | |
| font-size: 1rem; | |
| } | |
| .gr-button-primary { | |
| width: 100%; | |
| padding: 0.7rem; | |
| } | |
| } | |
| """ | |
| with gr.Blocks(theme=theme, css=css, title="🗓️ Workload Estimator") as demo: | |
| with gr.Column(elem_id="app-container"): | |
| gr.Markdown( | |
| """ | |
| <div id="app-header"> | |
| <h1>🗓️ Calendar Workload Estimator</h1> | |
| <p>Estimate your daily cognitive load based on meetings, focus time, and recovery.</p> | |
| </div> | |
| """, | |
| elem_id="header" | |
| ) | |
| with gr.Tab("📊 Estimate Your Day"): | |
| with gr.Row(equal_height=False): | |
| # VASEN SARake: dropdown + sliderit + nappi | |
| with gr.Column(scale=2): | |
| example_dropdown = gr.Dropdown( | |
| label="💡 Load example schedule", | |
| choices=list(EXAMPLE_PRESETS.keys()), | |
| value=None, | |
| interactive=True, | |
| ) | |
| gr.Markdown("### 🗓️ Workday Structure") | |
| meetings_count = gr.Slider(0, 12, value=4, step=1, label="Meetings (count)") | |
| total_meeting_hours = gr.Slider(0, 9, value=3, step=0.5, label="Total meeting hours") | |
| context_switches = gr.Slider(0, 20, value=6, step=1, label="Context switches") | |
| gr.Markdown("### 🧘 Focus & Recovery") | |
| deep_work_blocks = gr.Slider(0, 4, value=1, step=1, label="Deep work blocks (≥60 min)") | |
| break_minutes = gr.Slider(0, 120, value=30, step=5, label="Total break minutes") | |
| gr.Markdown("### ⏰ Workday Timing") | |
| day_start_hour = gr.Slider(6, 11, value=9, step=1, label="Start hour (24h)") | |
| day_end_hour = gr.Slider(14, 21, value=17, step=1, label="End hour (24h)") | |
| btn = gr.Button("🔍 Analyze Workload", variant="primary") | |
| # OIKEA sarake: tulos | |
| with gr.Column(scale=1): | |
| gr.Markdown("### 📈 Result") | |
| headline_out = gr.HTML() | |
| probs_out = gr.Label(label="Workload Probabilities") | |
| explanation_out = gr.HTML() | |
| # Kun valitaan esimerkki → täytetään sliderit | |
| example_dropdown.change( | |
| load_example, | |
| inputs=example_dropdown, | |
| outputs=[ | |
| meetings_count, | |
| total_meeting_hours, | |
| context_switches, | |
| deep_work_blocks, | |
| break_minutes, | |
| day_start_hour, | |
| day_end_hour, | |
| ], | |
| ) | |
| # Varsinainen ennustekutsu | |
| btn.click( | |
| predict_workload, | |
| inputs=[ | |
| meetings_count, | |
| total_meeting_hours, | |
| context_switches, | |
| deep_work_blocks, | |
| break_minutes, | |
| day_start_hour, | |
| day_end_hour, | |
| ], | |
| outputs=[headline_out, probs_out, explanation_out], | |
| ) | |
| with gr.Tab("ℹ️ About"): | |
| gr.Markdown(""" | |
| ### About This Tool | |
| This demo shows how **calendar metadata** can be used to estimate cognitive workload — helping you reflect on sustainability, focus, and recovery. | |
| - **Synthetic data only**: No real user data was used. | |
| - **Model**: Trained `RandomForestClassifier` (scikit-learn). | |
| - **Output**: 3-class workload (`Low`, `Medium`, `High`). | |
| - **Goal**: Spark reflection, not replace judgment. | |
| Use this to **simulate "what-if" scenarios**: | |
| _"What if I cancel two meetings?"_ or _"What if I add a 90-min focus block?"_ | |
| Built with **Python**, **scikit-learn** and **Gradio**, deployed on **Hugging Face Spaces**. | |
| """) | |
| if __name__ == "__main__": | |
| demo.launch() | |