Skip to main content

WaveDB / To-do App

A multi-user To-do list application using WaveDB for data management. This example is very similar to the todo.py example, except that this example uses WaveDB instead of an in-memory list.

from h2o_wave import main, app, Q, ui, connect, WaveDB, expando_to_dict


# A simple class that represents a to-do item.
class TodoItem:
def __init__(self, id, label, done):
self.id = id
self.label = label
self.done = done


async def setup_db() -> WaveDB:
db = connect()['todo']
_, err = await db.exec_atomic(
"""
create table if not exists todo (
id integer primary key,
user text not null,
label text not null,
done integer not null default 0
)
"""
)
if err:
raise RuntimeError(f'Failed setting up database: {err}')
return db


@app('/demo')
async def serve(q: Q):
if q.app.db is None:
q.app.db = await setup_db()

if q.args.new_todo: # Display an input form.
await new_todo(q)
elif q.args.add_todo: # Add an item.
await add_todo(q)
else: # Show all items.
await show_todos(q)


async def show_todos(q: Q):
# Get items for this user.
db: WaveDB = q.app.db

# Check if we have any updates, i.e. the user has checked/unchecked any item.
updates = []
for key, done in expando_to_dict(q.args).items():
# We've named each todo item `todo_{id}' (e.g. todo_42, todo_43, and so on)
# So identify the todo items from their 'todo_' prefix, then extract the ids from the names.
if key.startswith('todo_'):
_, id = key.split('_', 1)
updates.append(('update todo set done=? where id=?', 1 if done else 0, int(id)))

# If we have updates, update our database.
if len(updates):
_, err = await db.exec_many(*updates)
if err:
raise RuntimeError(f'Failed updating todos: {err}')

# Fetch latest todos for our user
rows, err = await db.exec('select id, label, done from todo where user=?', q.auth.subject)
if err:
raise RuntimeError(f'Failed fetching todos: {err}')
todos = [TodoItem(id, label, done) for id, label, done in rows]

# Create done/not-done checkboxes.
done = [ui.checkbox(name=f'todo_{todo.id}', label=todo.label, value=True, trigger=True) for todo in todos if
todo.done]
not_done = [ui.checkbox(name=f'todo_{todo.id}', label=todo.label, trigger=True) for todo in todos if not todo.done]

# Display list
q.page['form'] = ui.form_card(box='1 1 4 10', items=[
ui.text_l('To Do'),
ui.button(name='new_todo', label='Add To Do...', primary=True),
*not_done,
*([ui.separator('Done')] if len(done) else []),
*done,
])
await q.page.save()


async def add_todo(q: Q):
# Insert a new item
db: WaveDB = q.app.db
_, err = await db.exec('insert into todo (user, label) values (? , ?)', q.auth.subject, q.args.label or 'Untitled')
if err:
raise RuntimeError(f'Failed inserting todo: {err}')

# Go back to our list.
await show_todos(q)


async def new_todo(q: Q):
# Display an input form
q.page['form'] = ui.form_card(box='1 1 4 10', items=[
ui.text_l('Add To Do'),
ui.textbox(name='label', label='What needs to be done?', multiline=True),
ui.buttons([
ui.button(name='add_todo', label='Add', primary=True),
ui.button(name='show_todos', label='Back'),
]),
])
await q.page.save()