Part 2 - Todo Lists!
Lets build our second skill, this time something more useful - todo lists
Overview
In this part we will create our first proper, useful skill.
This skill will allow us to:
- create a todo list that we can add items to
- ask our AI to tell us whats on our list
- remove items from the list
Table of Contents
- Overview
- Table of Contents
- Video on YouTube
- Todo lists
- Example Conversational flow
- Code
- todo.py
- alf.py
Video on YouTube
I created a popular video series on YouTube, which goes over each of the steps below. The second video part is featured below.
Todo lists
Lets break down the task of creating our new skill into the following parts. This process is call decomposition, because we are de-composing the parts that make up the code:
- Create an
Todo-itemclass - to model a single todo item - Create a
Todo-class- to model a list of todo items - Add items
- List items
- Remove items
- Iterate items - make the list iterable using generators
- Add a
metaclass:Len- to return number of items in the list
Example Conversational flow
It helps to start with the end in mind, below is what we want a typical conversation to look like:
Code
Grab a copy of version 2 of our code here: https://github.com/kevinmcaleer/PythonAI/releases/tag/v2.0
Our skill is made up of the following classes:
Status- this is used to define a number of states our item can be inPriority- this is used to define a number of priorities our item can haveItem- represents a todo itemTodo- contains a list of our todo items, and has the functions for adding, removing and retrieving items
todo.py
from datetime import date
from enum import Enum
from uuid import uuid4
class Status(Enum):
""" The Todo Statuses """
NOT_STARTED = 0
IN_PROGRESS = 1
COMPLETED = 2
class Priority(Enum):
LOW = 0
MEDIUM = 1
HIGH = 2
class Item():
__creation_date = date.today()
__title = "empty"
__status = Status.NOT_STARTED
__priority = Priority.LOW
__flag = False
__url = ""
__due_date = date
__icon = ""
__state = False
__notes = ""
def __init__(self, title:str=None):
if title is not None:
self.__title = title
self.__id = str(uuid4())
@property
def title(self):
return self.__title
@title.setter
def title(self, value:str):
self.__title = value
@property
def priority(self):
return self.__priority.name
@priority.setter
def priority(self, value:Priority):
self.__priority = value
@property
def creation_date(self):
return self.__creation_date
@creation_date.setter
def creation_date(self, value):
self.__creation_date = value
@property
def age(self):
return self.__creation_date - date.today()
@property
def status(self):
return self.__status.name
@status.setter
def status(self, value:Status):
self.__status = value
@property
def id(self):
return self.__id
@property
def flag(self):
return self.__flag
@flag.setter
def flag(self, value:bool):
self.__flag = value
@property
def url(self):
return self.__url
@url.setter
def url(self, value:str):
self.__url = value
@property
def due_date(self):
return self.__due_date
@due_date.setter
def due_date(self, value:date):
self.__due_date = value
@property
def icon(self):
return self.__icon
@icon.setter
def icon(self, value:str):
self.__icon = value
@property
def state(self):
return self.__state
@state.setter
def state(self, value:bool):
self.__state = value
@property
def notes(self):
return self.__notes
@notes.setter
def notes(self, value:str):
self.__value = value
class Todo():
__todos = []
def __init__(self):
print("new todo list created")
self._current = -1
def __iter__(self):
return self
def __next__(self):
if self._current < len(self.__todos) -1 :
self._current += 1
print(self.__todos[self._current].title)
return self.__todos[self._current]
else:
self._current = -1
raise StopIteration
def __len__(self):
""" Returns the number of items in the Todo List """
return len(self.__todos)
def new_item(self, item:Item):
self.__todos.append(item)
@property
def items(self)->list:
return self.__todos
def show(self):
print("*"*80)
for item in self.__todos:
print(item.title, item.status, item.priority, item.age)
@classmethod
def show(cls):
print("*"*80)
print("Todo Items")
print('*'*80)
count = 1
if len(cls.__todos) == 0:
print("No items in list!")
else:
for item in cls.__todos:
print(count, item.title, item.status, item.priority, item.age, item.id)
count += 1
print("")
def remove_item(self, uuid:str=None, title:str=None)->bool:
if title is None and uuid is None:
print("You need to provide some details for me to remote it, either UUID or title")
if uuid is None and title:
for item in self.__todos:
if item.title == title:
# del self.__todos[item
self.__todos.remove(item)
return True
print("Item with title", title, 'not found')
return False
if uuid:
self.__todos.remove(uuid)
return True
alf.py
Lets update our alf.py to include the new skill and some commands for using it.
import pyjokes
from ai import AI
from todo import Todo, Item
alf = AI()
todo = Todo()
# item = Item(title="get shopping")
# item2 = Item("potatoes")
# todo.new_item(item)
# todo.new_item(item2)
def joke():
funny = pyjokes.get_joke()
print(funny)
alf.say(funny)
def add_todo()->bool:
item = Item()
alf.say("Tell me what to add to the list")
try:
item.title = alf.listen()
todo.new_item(item)
message = "Added" + item.title
alf.say(message)
return True
except:
print("oops there was an error")
return False
def list_todos():
if len(todo) > 0:
alf.say("Here are your to do's")
for item in todo:
alf.say(item.title)
else:
alf.say("The list is empty!")
def remove_todo()->bool:
alf.say("Tell me which item to remove")
try:
item_title = alf.listen()
todo.remove_item(title=item_title)
message = "Removed" + item_title
alf.say(message)
return True
except:
print("opps there was an error")
return False
command = ""
while True and command != "goodbye":
try:
command = alf.listen()
command = command.lower()
except:
print("oops there was an error")
command = ""
print("command was:", command)
if command == "tell me a joke":
joke()
command = ""
if command in ["add to-do","add to do", "add item"]:
add_todo()
command = ""
if command in ["list todos", "list todo", "list to do", "list to-do", "list to do's",'list items']:
list_todos()
command = ""
if command in ["remove todo", "remove item", "mark done", "remove todos", "remove to-do", "remove to do's"]:
remove_todo()
alf.say("Goodbye, I'm going to sleep now")

Comments