"""
Schedule & Note Management Web App
A lightweight Gradio 6 application for managing tasks, events, reminders, and fees.
Based on Product Requirements Document v1.0
"""
import gradio as gr
import json
from datetime import datetime, timedelta
from typing import Optional
from enum import Enum
# ============================================================================
# DATA MODELS AND STATE MANAGEMENT
# ============================================================================
class TaskPriority(str, Enum):
LOW = "Low"
MEDIUM = "Medium"
HIGH = "High"
class TaskStatus(str, Enum):
PENDING = "Pending"
COMPLETED = "Completed"
class ReminderType(str, Enum):
NONE = "None"
BEFORE_15_MIN = "15 minutes before"
BEFORE_30_MIN = "30 minutes before"
BEFORE_1_HOUR = "1 hour before"
BEFORE_1_DAY = "1 day before"
# Sample data for demonstration
SAMPLE_TASKS = [
{
"id": "1",
"title": "Team Meeting",
"description": "Weekly sync with the development team",
"date": datetime.now().strftime("%Y-%m-%d"),
"time": "09:00",
"priority": TaskPriority.HIGH.value,
"status": TaskStatus.PENDING.value,
"fee": 0.0,
"reminder": ReminderType.BEFORE_15_MIN.value,
"notes": "Prepare agenda beforehand",
"created_at": datetime.now().isoformat()
},
{
"id": "2",
"title": "Client Presentation",
"description": "Present the new product roadmap",
"date": datetime.now().strftime("%Y-%m-%d"),
"time": "14:00",
"priority": TaskPriority.HIGH.value,
"status": TaskStatus.PENDING.value,
"fee": 150.0,
"reminder": ReminderType.BEFORE_1_HOUR.value,
"notes": "Review slides and prepare demo",
"created_at": datetime.now().isoformat()
},
{
"id": "3",
"title": "Code Review",
"description": "Review PRs for the new feature branch",
"date": (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d"),
"time": "11:00",
"priority": TaskPriority.MEDIUM.value,
"status": TaskStatus.PENDING.value,
"fee": 0.0,
"reminder": ReminderType.BEFORE_30_MIN.value,
"notes": "Check for security vulnerabilities",
"created_at": datetime.now().isoformat()
},
{
"id": "4",
"title": "Gym Session",
"description": "Weekly workout routine",
"date": datetime.now().strftime("%Y-%m-%d"),
"time": "18:00",
"priority": TaskPriority.LOW.value,
"status": TaskStatus.COMPLETED.value,
"fee": 50.0,
"reminder": ReminderType.NONE.value,
"notes": "Focus on cardio today",
"created_at": datetime.now().isoformat()
}
]
def get_next_id(tasks: list) -> str:
"""Generate a new unique ID for tasks."""
if not tasks:
return "1"
max_id = max(int(task["id"]) for task in tasks)
return str(max_id + 1)
def calculate_weekly_stats(tasks: list) -> dict:
"""Calculate statistics for the current week."""
now = datetime.now()
week_start = now - timedelta(days=now.weekday())
week_end = week_start + timedelta(days=6)
weekly_tasks = [
t for t in tasks
if week_start.strftime("%Y-%m-%d") <= t["date"] <= week_end.strftime("%Y-%m-%d")
]
completed = sum(1 for t in weekly_tasks if t["status"] == TaskStatus.COMPLETED.value)
total_fee = sum(float(t.get("fee", 0) or 0) for t in weekly_tasks)
return {
"total_tasks": len(weekly_tasks),
"completed": completed,
"pending": len(weekly_tasks) - completed,
"total_fee": total_fee,
"completion_rate": round((completed / len(weekly_tasks) * 100), 1) if weekly_tasks else 0
}
def get_weekly_schedule(tasks: list) -> str:
"""Generate a weekly schedule display."""
now = datetime.now()
days = []
for i in range(7):
day = now + timedelta(days=i)
day_str = day.strftime("%Y-%m-%d")
day_name = day.strftime("%A")
is_today = i == 0
day_tasks = [
f" ⢠**{t['time']}** - {t['title']}"
for t in sorted(tasks, key=lambda x: x.get("time", ""))
if t["date"] == day_str
]
day_header = f"**{day_name} {day.strftime('%b %d')}" + (' (Today)' if is_today else '') + "**"
day_content = "\n".join(day_tasks) if day_tasks else " No tasks scheduled"
days.append(f"{day_header}\n{day_content}")
return "\n\n".join(days)
# ============================================================================
# APPLICATION STATE
# ============================================================================
class AppState:
"""Application state manager for Gradio 6."""
def __init__(self):
self.tasks = SAMPLE_TASKS.copy()
def get_tasks(self) -> list:
"""Get all tasks."""
return self.tasks
def add_task(self, task: dict) -> None:
"""Add a new task."""
task["id"] = get_next_id(self.tasks)
task["created_at"] = datetime.now().isoformat()
self.tasks.append(task)
def update_task(self, task_id: str, updates: dict) -> None:
"""Update an existing task."""
for task in self.tasks:
if task["id"] == task_id:
task.update(updates)
break
def delete_task(self, task_id: str) -> None:
"""Delete a task by ID."""
self.tasks = [t for t in self.tasks if t["id"] != task_id]
def get_task_by_id(self, task_id: str) -> Optional[dict]:
"""Get a task by its ID."""
for task in self.tasks:
if task["id"] == task_id:
return task
return None
# Initialize global state
app_state = AppState()
# ============================================================================
# HELPER FUNCTIONS
# ============================================================================
def refresh_task_list() -> dict:
"""Refresh the task list and return current state."""
tasks = app_state.get_tasks()
return {
task_list: create_task_cards(tasks),
task_count: f"Total Tasks: {len(tasks)}",
weekly_stats: create_weekly_stats_display(),
weekly_schedule: get_weekly_schedule(tasks)
}
def create_task_cards(tasks: list) -> str:
"""Create HTML cards for task display."""
if not tasks:
return """
š No tasks yet
Create your first task to get started!
"""
cards = []
sorted_tasks = sorted(tasks, key=lambda x: (x.get("date", ""), x.get("time", "")))
for task in sorted_tasks:
priority_colors = {
"Low": "#28a745",
"Medium": "#ffc107",
"High": "#dc3545"
}
status_colors = {
"Pending": "#6c757d",
"Completed": "#28a745"
}
priority_color = priority_colors.get(task.get("priority", "Medium"), "#6c757d")
status_color = status_colors.get(task.get("status", "Pending"), "#6c757d")
card = f"""
{task['title']}
{task.get('priority', 'Medium')}
{task.get('status', 'Pending')}
š
{task.get('date', 'No date')}
š {task.get('time', 'No time')}
{task.get('description', 'No description')}
{'š° Fee: $' + str(task.get('fee', 0)) if float(task.get('fee', 0)) > 0 else ''}
{' | ā° Reminder: ' + task.get('reminder', 'None') if task.get('reminder') != 'None' else ''}
ID: {task['id']}
"""
cards.append(card)
return "\n".join(cards)
def create_weekly_stats_display() -> str:
"""Create weekly statistics display."""
stats = calculate_weekly_stats(app_state.get_tasks())
return f"""
{stats['total_tasks']}
This Week's Tasks
{stats['completed']}
Completed
{stats['pending']}
Pending
${stats['total_fee']:.2f}
Total Fees
"""
# ============================================================================
# TASK OPERATIONS
# ============================================================================
def add_new_task(
title: str,
description: str,
task_date: str,
task_time: str,
priority: str,
fee: float,
reminder: str,
notes: str
) -> dict:
"""Add a new task to the system."""
if not title or not task_date or not task_time:
return {
task_list: create_task_cards(app_state.get_tasks()),
status_msg: gr.update(value="ā Please fill in all required fields!", visible=True),
form_message: gr.update(value="ā Error: Title, date, and time are required", visible=True)
}
new_task = {
"title": title,
"description": description,
"date": task_date,
"time": task_time,
"priority": priority,
"status": TaskStatus.PENDING.value,
"fee": fee,
"reminder": reminder,
"notes": notes
}
app_state.add_task(new_task)
return {
task_list: create_task_cards(app_state.get_tasks()),
status_msg: gr.update(value="ā
Task created successfully!", visible=True),
form_message: gr.update(value="ā
Task added successfully!", visible=True),
# Clear form
title_input: gr.update(value=""),
description_input: gr.update(value=""),
fee_input: gr.update(value=0.0),
notes_input: gr.update(value="")
}
def update_task_status(task_id: str, new_status: str) -> dict:
"""Update a task's status."""
if task_id and new_status:
app_state.update_task(task_id, {"status": new_status})
return {
task_list: create_task_cards(app_state.get_tasks()),
status_msg: gr.update(value=f"ā
Task {task_id} marked as {new_status}", visible=True)
}
def delete_task_action(task_id: str) -> dict:
"""Delete a task."""
if task_id:
app_state.delete_task(task_id)
return {
task_list: create_task_cards(app_state.get_tasks()),
status_msg: gr.update(value=f"ā
Task deleted successfully", visible=True)
}
def load_task_for_edit(task_id: str) -> dict:
"""Load task data into the edit form."""
task = app_state.get_task_by_id(task_id)
if task:
return {
edit_task_id: task_id,
edit_title: gr.update(value=task["title"]),
edit_description: gr.update(value=task.get("description", "")),
edit_date: gr.update(value=task.get("date", "")),
edit_time: gr.update(value=task.get("time", "")),
edit_priority: gr.update(value=task.get("priority", "Medium")),
edit_fee: gr.update(value=float(task.get("fee", 0) or 0)),
edit_reminder: gr.update(value=task.get("reminder", "None")),
edit_notes: gr.update(value=task.get("notes", "")),
show_edit_form: gr.update(visible=True)
}
return {show_edit_form: gr.update(visible=False)}
def save_edited_task(
task_id: str,
title: str,
description: str,
task_date: str,
task_time: str,
priority: str,
fee: float,
reminder: str,
notes: str
) -> dict:
"""Save edited task."""
if task_id and title:
updates = {
"title": title,
"description": description,
"date": task_date,
"time": task_time,
"priority": priority,
"fee": fee,
"reminder": reminder,
"notes": notes
}
app_state.update_task(task_id, updates)
return {
task_list: create_task_cards(app_state.get_tasks()),
status_msg: gr.update(value="ā
Task updated successfully!", visible=True),
show_edit_form: gr.update(visible=False)
}
def filter_tasks(status_filter: str, priority_filter: str) -> dict:
"""Filter tasks by status and priority."""
tasks = app_state.get_tasks()
if status_filter != "All":
tasks = [t for t in tasks if t.get("status") == status_filter]
if priority_filter != "All":
tasks = [t for t in tasks if t.get("priority") == priority_filter]
return {
task_list: create_task_cards(tasks)
}
def search_tasks(search_query: str) -> dict:
"""Search tasks by title or description."""
if not search_query.strip():
return {task_list: create_task_cards(app_state.get_tasks())}
query = search_query.lower()
tasks = [
t for t in app_state.get_tasks()
if query in t.get("title", "").lower() or query in t.get("description", "").lower()
]
return {
task_list: create_task_cards(tasks)
}
# ============================================================================
# GRADIO 6 APPLICATION
# ============================================================================
# Custom theme for the application
custom_theme = gr.themes.Soft(
primary_hue="indigo",
secondary_hue="purple",
neutral_hue="slate",
font=gr.themes.GoogleFont("Inter"),
text_size="lg",
spacing_size="lg",
radius_size="md"
).set(
button_primary_background_fill="*primary_600",
button_primary_background_fill_hover="*primary_700",
button_secondary_background_fill="*secondary_100",
button_secondary_background_fill_hover="*secondary_200",
block_title_text_weight="600",
block_background_fill="white",
block_radius="12px",
)
with gr.Blocks(theme=custom_theme) as demo:
# Header
gr.HTML("""
š
Schedule & Note Management
Plan tasks, manage events, and track fees efficiently
""")
gr.HTML("""
""")
# Main content with tabs
with gr.Tabs() as tabs:
# Tab 1: Dashboard
with gr.TabItem("š Dashboard", id="dashboard"):
gr.Markdown("### š Weekly Overview")
with gr.Row():
with gr.Column(scale=2):
weekly_stats_html = gr.HTML(
value=create_weekly_stats_display(),
elem_id="weekly-stats"
)
with gr.Column(scale=1):
gr.Markdown("### š
Quick Stats")
task_count = gr.Markdown(f"**Total Tasks: {len(app_state.get_tasks())}**")
gr.Markdown("### š This Week's Schedule")
weekly_schedule = gr.Markdown(
value=get_weekly_schedule(app_state.get_tasks()),
elem_id="weekly-schedule"
)
status_msg = gr.Markdown(visible=False, value="")
# Tab 2: Task Management
with gr.TabItem("ā
Tasks", id="tasks"):
gr.Markdown("### ā
Task Management")
# Search and filter row
with gr.Row():
with gr.Column(scale=3):
search_input = gr.Textbox(
label="š Search Tasks",
placeholder="Search by title or description...",
elem_id="search-input"
)
with gr.Column(scale=1):
status_filter = gr.Dropdown(
choices=["All", "Pending", "Completed"],
value="All",
label="Status"
)
with gr.Column(scale=1):
priority_filter = gr.Dropdown(
choices=["All", "Low", "Medium", "High"],
value="All",
label="Priority"
)
# Task list display
task_list = gr.HTML(
value=create_task_cards(app_state.get_tasks()),
elem_id="task-list"
)
status_msg = gr.Markdown(visible=False, value="")
# Tab 3: Add New Task
with gr.TabItem("ā Add Task", id="add-task"):
gr.Markdown("### ā Create New Task")
with gr.Row():
with gr.Column(scale=2):
title_input = gr.Textbox(
label="Task Title *",
placeholder="Enter task title",
elem_id="title-input"
)
description_input = gr.Textbox(
label="Description",
placeholder="Enter task description",
lines=3,
elem_id="description-input"
)
with gr.Column(scale=1):
task_date = gr.DateTime(
label="Date & Time *",
type="datetime",
elem_id="task-date"
)
task_time = gr.Textbox(
label="Time (HH:MM) *",
placeholder="09:00",
value="09:00",
elem_id="task-time"
)
with gr.Row():
with gr.Column():
priority = gr.Radio(
choices=["Low", "Medium", "High"],
value="Medium",
label="Priority"
)
with gr.Column():
fee_input = gr.Number(
label="Fee/Cost ($)",
value=0.0,
minimum=0.0,
elem_id="fee-input"
)
with gr.Column():
reminder = gr.Dropdown(
choices=["None", "15 minutes before", "30 minutes before",
"1 hour before", "1 day before"],
value="None",
label="Reminder"
)
notes_input = gr.Textbox(
label="Notes",
placeholder="Additional notes or comments...",
lines=2,
elem_id="notes-input"
)
form_message = gr.Markdown(visible=False, value="")
with gr.Row():
add_btn = gr.Button("Create Task", variant="primary", size="lg")
clear_btn = gr.Button("Clear Form", variant="secondary")
# Tab 4: Edit Task
with gr.TabItem("āļø Edit Task", id="edit-task"):
gr.Markdown("### āļø Edit Task")
# Task selection dropdown
task_selector = gr.Dropdown(
choices=[(f"{t['title']} ({t.get('date', 'No date')}) - ID:{t['id']}", t['id'])
for t in app_state.get_tasks()],
label="Select Task to Edit",
allow_custom_value=True,
elem_id="task-selector"
)
show_edit_form = gr.Column(visible=False, elem_id="edit-form-container")
with show_edit_form:
edit_task_id = gr.Textbox(visible=False, elem_id="edit-task-id")
with gr.Row():
with gr.Column(scale=2):
edit_title = gr.Textbox(
label="Task Title *",
placeholder="Enter task title",
elem_id="edit-title"
)
edit_description = gr.Textbox(
label="Description",
placeholder="Enter task description",
lines=3,
elem_id="edit-description"
)
with gr.Column(scale=1):
edit_date = gr.Textbox(
label="Date (YYYY-MM-DD) *",
placeholder="2024-01-15",
elem_id="edit-date"
)
edit_time = gr.Textbox(
label="Time (HH:MM) *",
placeholder="09:00",
elem_id="edit-time"
)
with gr.Row():
with gr.Column():
edit_priority = gr.Radio(
choices=["Low", "Medium", "High"],
value="Medium",
label="Priority"
)
with gr.Column():
edit_fee = gr.Number(
label="Fee/Cost ($)",
value=0.0,
minimum=0.0,
elem_id="edit-fee"
)
with gr.Column():
edit_reminder = gr.Dropdown(
choices=["None", "15 minutes before", "30 minutes before",
"1 hour before", "1 day before"],
value="None",
label="Reminder"
)
edit_notes = gr.Textbox(
label="Notes",
placeholder="Additional notes or comments...",
lines=2,
elem_id="edit-notes"
)
with gr.Row():
save_btn = gr.Button("Save Changes", variant="primary", size="lg")
cancel_btn = gr.Button("Cancel", variant="secondary")
# Tab 5: Notes Manager
with gr.TabItem("š Notes", id="notes"):
gr.Markdown("### š Quick Notes")
gr.Markdown("*Notes are attached to individual tasks. View task notes in the Tasks tab.*")
with gr.Row():
quick_note = gr.Textbox(
label="Quick Note",
placeholder="Write a quick note...",
lines=4
)
note_display = gr.Markdown(
value="**Recent Notes**\n\n⢠Review meeting notes from Monday\n⢠Update project timeline\n⢠Prepare budget report",
elem_id="note-display"
)
with gr.Row():
save_note_btn = gr.Button("Save Note", variant="primary")
clear_note_btn = gr.Button("Clear", variant="secondary")
# Tab 6: Fees Summary
with gr.TabItem("š° Fees", id="fees"):
gr.Markdown("### š° Fee Tracking Summary")
# Fee statistics
tasks = app_state.get_tasks()
total_fees = sum(float(t.get("fee", 0) or 0) for t in tasks)
task_with_fees = sum(1 for t in tasks if float(t.get("fee", 0) or 0) > 0)
avg_fee = total_fees / task_with_fees if task_with_fees > 0 else 0
gr.HTML(f"""
${total_fees:.2f}
Total Fees
{task_with_fees}
Tasks with Fees
${avg_fee:.2f}
Average Fee
""")
gr.Markdown("### šµ Tasks with Fees")
fee_task_list = gr.HTML(
value=create_task_cards([t for t in tasks if float(t.get("fee", 0) or 0) > 0]),
elem_id="fee-task-list"
)
# ============================================================================
# EVENT HANDLERS
# ============================================================================
# Search and filter handlers
search_input.submit(search_tasks, inputs=search_input, outputs=[task_list])
status_filter.change(filter_tasks, inputs=[status_filter, priority_filter], outputs=[task_list])
priority_filter.change(filter_tasks, inputs=[status_filter, priority_filter], outputs=[task_list])
# Add task handlers
add_btn.click(
add_new_task,
inputs=[title_input, description_input, task_date, task_time,
priority, fee_input, reminder, notes_input],
outputs=[task_list, status_msg, form_message, title_input,
description_input, fee_input, notes_input]
)
clear_btn.click(
lambda: {
title_input: gr.update(value=""),
description_input: gr.update(value=""),
fee_input: gr.update(value=0.0),
notes_input: gr.update(value=""),
form_message: gr.update(visible=False)
},
outputs=[title_input, description_input, fee_input, notes_input, form_message]
)
# Task selector for editing
task_selector.select(
load_task_for_edit,
inputs=task_selector,
outputs=[edit_task_id, edit_title, edit_description, edit_date, edit_time,
edit_priority, edit_fee, edit_reminder, edit_notes, show_edit_form]
)
# Save edited task
save_btn.click(
save_edited_task,
inputs=[edit_task_id, edit_title, edit_description, edit_date, edit_time,
edit_priority, edit_fee, edit_reminder, edit_notes],
outputs=[task_list, status_msg, show_edit_form]
)
# Cancel edit
cancel_btn.click(
lambda: {show_edit_form: gr.update(visible=False)},
outputs=[show_edit_form]
)
# Notes handlers
save_note_btn.click(
lambda note: {
note_display: note_display.value + f"\n⢠{note}" if note else note_display.value
},
inputs=quick_note,
outputs=[note_display]
)
clear_note_btn.click(
lambda: {quick_note: gr.update(value="")},
outputs=[quick_note]
)
# Refresh button (hidden, used for updates)
refresh_btn = gr.Button("Refresh", visible=False)
refresh_btn.click(refresh_task_list, outputs=[task_list, task_count, weekly_stats_html, weekly_schedule])
# ============================================================================
# LAUNCH APPLICATION
# ============================================================================
demo.launch(
theme=gr.themes.Soft(
primary_hue="indigo",
secondary_hue="purple",
neutral_hue="slate",
font=gr.themes.GoogleFont("Inter"),
text_size="lg",
spacing_size="lg",
radius_size="md"
),
title="Schedule & Note Management App",
description="A lightweight application for managing tasks, events, reminders, and fees. Based on PRD v1.0",
css="""
.gradio-container {
max-width: 1200px !important;
}
#weekly-stats, #fee-task-list {
margin-bottom: 20px;
}
#search-input {
border-radius: 8px;
}
""",
footer_links=[
{"label": "Documentation", "url": "#"},
{"label": "Support", "url": "#"},
{"api": "api"}
]
)