{"assignment":{"_schema_version":2,"course_id":37,"date_created":"2025-10-09T18:00:00+00:00","date_modified":"2025-10-23T14:22:02.475645+00:00","extra_instructor_files":"{}","extra_starting_files":"","forked_id":null,"forked_version":null,"hidden":false,"id":2545,"instructions":"# Todo List\n\n## Another Example\n\n-   Todo List: Keep track of tasks.\n-   [todo.py](https://gist.githubusercontent.com/acbart/8bfb48903b6e5a5073a32d602afdff4a/raw/be48bf598b7a6312a5b261e4578ed11a4e035565/todo.py)\n\nIn this example program, we created a tool for managing a todo list. You can add tasks, mark them as complete, edit them, and remove them from the list. This would be a useful tool for anyone who needs to keep track of their tasks and stay organized.\n\nThis is probably the most complex program we've created so far, so we once again strongly recommend that you download the code and run it in Thonny.\n\n## The Code\n\n```python todo\nfrom drafter import *\n\n\n@dataclass\nclass TodoItem:\n    \"\"\"\n    An individual list item that should be done.\n\n    Attributes:\n        id (int): A unique identifier for the to-do item.\n        name (str): The name of the to-do item.\n        description (str): A detailed description of the to-do item.\n        completed (bool): A boolean indicating whether the to-do item has been completed.\n        difficulty (int): An integer rating (1-3) indicating the difficulty of the to-do item.\n    \"\"\"\n\n    id: int\n    name: str\n    description: str\n    completed: bool\n    difficulty: int\n\n\n@dataclass\nclass State:\n    \"\"\"\n    The state of the to-do application, containing a list of to-do items.\n\n    Attributes:\n        count (int): The total number of to-do items that have ever been created. Used to assign unique IDs.\n        todos (list[TodoItem]): A list of TodoItem objects representing the to-do items.\n    \"\"\"\n\n    count: int\n    todos: list[TodoItem]\n\n\n@route\ndef index(state: State) -> Page:\n    \"\"\"\n    Display the main to-do list page.\n\n    Args:\n        state (State): The current state of the to-do application.\n\n    Returns:\n        Page: The rendered to-do list page.\n    \"\"\"\n    current_items = make_todo_list(state.todos)\n\n    return Page(\n        state,\n        [\n            Header(\"To-Do List\"),\n            \"Add and manage your to-do items.\",\n            Button(\"Add New To-Do\", \"add_todo\"),\n            Div(current_items),\n        ],\n    )\n\n\ndef make_todo_toggle(completed: bool, target_id: Argument) -> Button:\n    \"\"\"\n    Creates a button to toggle the completion status of a to-do item.\n\n    Args:\n        completed (bool): The current completion status of the to-do item.\n        target_id (Argument): The unique ID of the to-do item.\n    Returns:\n        Button: A button that displays a checked or unchecked box based on the completion status.\n    \"\"\"\n    if completed:\n        return Button(\"\u2611\ufe0f\", \"toggle_complete\", target_id)\n    else:\n        return Button(\"\ud83d\udd32\", \"toggle_complete\", target_id)\n\n\ndef make_todo_list(todos: list[TodoItem]) -> PageContent:\n    \"\"\"\n    Generates a page content displaying a list of to-do items.\n\n    Args:\n        todos (list[TodoItem]): A list of TodoItem objects representing the to-do items.\n\n    Returns:\n        PageContent: A page content object containing either a message indicating no to-do items,\n        or a table displaying each to-do item with its name, difficulty, description, and action buttons\n        for toggling completion, removing, and editing the item.\n    \"\"\"\n    if not todos:\n        return Div(\"No to-do items yet.\")\n\n    items = []\n    for todo in todos:\n        difficulty = \"\u2b50\" * todo.difficulty\n        target_id = Argument(\"target_id\", todo.id)\n        items.append(\n            [\n                make_todo_toggle(todo.completed, target_id),\n                Div(\n                    todo.name,\n                    difficulty,\n                    LineBreak(),\n                    small_font(Text(todo.description)),\n                ),\n                Button(\"Remove\", \"remove_todo\", target_id),\n                Button(\"Edit\", \"edit_todo\", target_id),\n            ]\n        )\n    return Table(items)\n\n\ndef lookup_todo_item(todos: list[TodoItem], target_id: int) -> TodoItem:\n    \"\"\"\n    Looks up a to-do item by its unique ID.\n\n    Args:\n        todos (list[TodoItem]): A list of TodoItem objects representing the to-do items.\n        target_id (int): The unique ID of the to-do item to look up.\n\n    Returns:\n        TodoItem: The to-do item with the specified ID.\n    \"\"\"\n    for todo in todos:\n        if todo.id == target_id:\n            return todo\n    return None\n\n\n@route\ndef toggle_complete(state: State, target_id: int) -> Page:\n    \"\"\"\n    Toggles the completion status of a to-do item.\n\n    Args:\n        state (State): The current state of the to-do application.\n        target_id (int): The unique ID of the to-do item to toggle.\n\n    Returns:\n        Page: The updated to-do list page.\n    \"\"\"\n    todo = lookup_todo_item(state.todos, target_id)\n    if todo:\n        todo.completed = not todo.completed\n    return index(state)\n\n\n@route\ndef remove_todo(state: State, target_id: int) -> Page:\n    \"\"\"\n    Removes a to-do item from the list.\n\n    Args:\n        state (State): The current state of the to-do application.\n        target_id (int): The unique ID of the to-do item to remove.\n\n    Returns:\n        Page: The updated to-do list page.\n    \"\"\"\n    new_todos = []\n    for todo in state.todos:\n        if todo.id != target_id:\n            new_todos.append(todo)\n    state.todos = new_todos\n    return index(state)\n\n\nDIFFICULTY_RATINGS = [\"1\", \"2\", \"3\"]\n\n\n@route\ndef edit_todo(state: State, target_id: int) -> Page:\n    \"\"\"\n    Displays the edit page for a specific to-do item.\n\n    Args:\n        state (State): The current state of the to-do application.\n        target_id (int): The unique ID of the to-do item to edit.\n    Returns:\n        Page: The edit page for the specified to-do item.\n    \"\"\"\n    editing = lookup_todo_item(state.todos, target_id)\n    return Page(\n        state,\n        [\n            Header(\"Edit:\"),\n            Row(\"Name:\", TextBox(\"new_name\", editing.name)),\n            Row(\n                \"Difficulty:\",\n                SelectBox(\n                    \"new_difficulty\", DIFFICULTY_RATINGS, str(editing.difficulty)\n                ),\n            ),\n            Row(\"Description:\", TextArea(\"new_description\", editing.description)),\n            Button(\"Save Changes\", \"save_changes\", Argument(\"target_id\", target_id)),\n            Button(\"Cancel\", \"index\"),\n        ],\n    )\n\n\n@route\ndef save_changes(\n    state: State,\n    new_name: str,\n    new_difficulty: int,\n    new_description: str,\n    target_id: int,\n) -> Page:\n    \"\"\"Saves the changes made to a to-do item.\n\n    Args:\n        state (State): The current state of the to-do application.\n        new_name (str): The new name for the to-do item.\n        new_difficulty (int): The new difficulty level for the to-do item.\n        new_description (str): The new description for the to-do item.\n        target_id (int): The unique ID of the to-do item to update.\n\n    Returns:\n        Page: The updated to-do list page.\n    \"\"\"\n    todo = lookup_todo_item(state.todos, target_id)\n    if todo:\n        todo.name = new_name\n        todo.difficulty = new_difficulty\n        todo.description = new_description\n\n    return index(state)\n\n\n@route\ndef add_todo(state: State) -> Page:\n    \"\"\"\n    Displays the page to add a new to-do item.\n    Args:\n        state (State): The current state of the to-do application.\n    Returns:\n        Page: The page to add a new to-do item.\n    \"\"\"\n    return Page(\n        state,\n        [\n            Header(\"Create New Todo\"),\n            Row(\"Name:\", TextBox(\"new_name\", \"\")),\n            Row(\n                \"Difficulty:\",\n                SelectBox(\"new_difficulty\", DIFFICULTY_RATINGS, \"1\"),\n            ),\n            Row(\"Description:\", TextArea(\"new_description\", \"\")),\n            Button(\"Save Changes\", \"save_new\"),\n            Button(\"Cancel\", \"index\"),\n        ],\n    )\n\n\n@route\ndef save_new(\n    state: State, new_name: str, new_difficulty: int, new_description: str\n) -> Page:\n    \"\"\"\n    Saves a new to-do item to the list. This will increase the count of total\n    to-do items ever created, and use that as the unique ID for the new item.\n\n    Args:\n        state (State): The current state of the to-do application.\n        new_name (str): The name of the new to-do item.\n        new_difficulty (int): The difficulty level of the new to-do item.\n        new_description (str): The description of the new to-do item.\n    Returns:\n        Page: The updated to-do list page.\n    \"\"\"\n    state.count += 1\n    new_todo = TodoItem(\n        state.count,\n        new_name,\n        new_description,\n        False,\n        new_difficulty,\n    )\n    state.todos.append(new_todo)\n    return index(state)\n\n\nstart_server(\n    State(\n        1, [TodoItem(0, \"Write todo list\", \"Write a few items to get done.\", False, 1)]\n    )\n)\n\n```\n\n## Unique IDs\n\nEach to-do item has a unique ID, which is an integer that is assigned when the item is created. The `State` class keeps track of the total number of to-do items that have ever been created with the `count` attribute. When a new to-do item is added, the `count` is incremented by 1, and that value is used as the ID for the new item. This ensures that each item has a unique identifier, even if items are removed from the list.\n\nThis pattern of using a counter to assign unique IDs is common in applications that manage collections of items. Thanks to that unique ID, we can easily look up, edit, and remove specific items from the list.\nFor that, we need to use the loop patterns we learned previously.\n\n## Create, Read, Update, Delete (CRUD)\n\n-   **Create**: The `add_todo` and `save_new` functions allow users to create new to-do items.\n-   **Read**: The `index` and `make_todo_list` functions display the current list of to-do items.\n-   **Update**: The `edit_todo`, `save_changes`, and `toggle_complete` functions allow users to modify existing to-do items.\n-   **Delete**: The `remove_todo` function allows users to delete to-do items from the list.\n\nA full-featured application typically involves four operations to manage data, often referred to as CRUD (Create, Read, Update, Delete). This to-do list application implements all four of these operations, allowing users to effectively manage their tasks. Almost any interesting application will need to implement CRUD operations in some form.\n\nIn this program, the creation is handled by the `add_todo` and `save_new` functions, reading is done by the `index` and `make_todo_list` functions, updating is managed by the `edit_todo`, `save_changes`, and `toggle_complete` functions, and deletion is performed by the `remove_todo` function.\n\nThere are also some common helper functions that are shared between these operations. For example, the `lookup_todo_item` function is used to find a specific to-do item by its unique ID. This function is used in several places, such as when toggling the completion status, editing an item, and saving changes.","ip_ranges":"","name":"9B2) Todo List","on_change":"","on_eval":"","on_run":"","owner_id":1,"owner_id__email":"acbart@udel.edu","points":0,"public":true,"reviewed":false,"sample_submissions":[],"settings":"{\"summary\": \"This is a fourth example of a longer Drafter program.\", \"small_layout\": true, \"disable_timeout\": true, \"hide_files\": false}","starting_code":"","subordinate":true,"tags":[],"type":"reading","url":"bakery_project2_example4_read","version":1},"ip":"216.73.216.157","submission":{"_schema_version":3,"assignment_id":2545,"assignment_version":1,"attempts":0,"code":"","correct":false,"course_id":37,"date_created":"2026-05-20T12:38:53.950649+00:00","date_due":"","date_graded":"","date_locked":"","date_modified":"2026-05-20T12:38:53.950649+00:00","date_started":"","date_submitted":"","endpoint":"","extra_files":"","feedback":"","grading_status":"NotReady","id":2036730,"score":0.0,"submission_status":"Started","time_limit":"","url":"submission_url-c43b8cd0-0222-4415-93cd-597427fdcc48","user_id":2044658,"user_id__email":"","version":0},"success":true}
