The Monty Project
The Monty Project is an educational software project designed to take you through the steps of building a small software incrementally, while applying as many Python and SE techniques as possible along the way.
The project aims to build a product named Monty, a Personal Assistant Chatbot that helps a person to keep track of various things. The name Monty was chosen as a placeholder name, in honor of the Monty Python comedy group whose work inspired the name of the the Python language. You may give it any other name and personality you wish.
Here is a sample interaction with Monty:
*******************************************************************************************
* __ __ _ _ __ __ _ *
* \ \ / / | | | | | \/ | | | *
* \ \ /\ / /__| | ___ ___ _ __ ___ ___ | |_ ___ | \ / | ___ _ __ | |_ _ _ *
* \ \/ \/ / _ \ |/ __/ _ \| '_ ' _ \ / _ \ | __/ _ \ | |\/| |/ _ \| '_ \| __| | | | *
* \ /\ / __/ | (_| (_) | | | | | | __/ | || (_) | | | | | (_) | | | | |_| |_| | *
* \/ \/ \___|_|\___\___/|_| |_| |_|\___| \__\___/ |_| |_|\___/|_| |_|\__|\__, | *
* __/ | *
* |___/ *
*******************************************************************************************
>>> What can I do for you?
help
>>> I'm glad you asked. Here it is:
==================================================
Monty can understand the following commands:
add DESCRIPTION
Adds a task with the DESCRIPTION to the list
Example: add read book
done INDEX
Marks the task at INDEX as 'done'
Example: done 1
exit
Exits the application
help
Shows the help information
list
Lists the tasks in the list
--------------------------------------------------
>>> What can I do for you?
add read book
>>> Task added to the list
>>> What can I do for you?
add return book
>>> Task added to the list
>>> What can I do for you?
done 1
>>> Congrats on completing a task! :-)
>>> What can I do for you?
list
>>> Here is the list of tasks:
==================================================
STATUS | INDEX | DESCRIPTION
--------------------------------------------------
X | 1 | read book
- | 2 | return book
--------------------------------------------------
>>> What can I do for you?
The project consists of the following increments:
- Levels: A series of features, meant to be added to Monty in the given order, although some can be skipped. These have been named
Level 1
toLevel 14
to indicate how each makes the product progressively "level up". - Extensions:
- Category B These are enhancements related to task tracking.
- Category C These are enhancements, not specifically related to task tracking.
- Category D Each of these adds the ability to track another type of entities.
Levels
Level 1 Echo Once
Write a Python program to read in one user command and repeat it back to the user. An example output is given below.
>>> Hello, my name is Monty
>>> What can I do for you?
foo
>>> Your command is: foo
>>> Bye!
Level 2 Recognize Known Commands
Extend Monty 1 code to work as follows:
- It recognizes the
list
command and responds with a fixed message. - It recognizes the
exit
command and exits the program if user confirms. - For all other inputs, it responds with a fixed error message.
A sample session is given below.
You are highly encouraged to give the software a different name (i.e., not Monty), define your own command formats (e.g., show
instead of list
), and even a different personality (e.g., you can make its questions/responses sound similar to a popular video game character). Differentiating your software in those ways will reduce the risk of plagiarism concerns.
>>> Hello, my name is Monty
>>> What can I do for you?
list
>>> Nothing to list
>>> What can I do for you?
foo
>>> OOPS! Unknown command
>>> What can I do for you?
exit
>>> Are you sure? y/n
n
>>> What can I do for you?
exit
>>> Are you sure? y/n
y
>>> Bye!
Level 3 Use Functions
Restructure the Monty Level 2 code to fit the following structure, while keeping the behavior same as before.
import sys
# ADD MISSING FUNCTIONS
def main():
print_greeting()
while True:
command = read_command()
execute_command(command)
main()
Level 4 Collect Tasks in Memory
Enhance the Monty Level 3 code to improve the functionality as per the sample output given below.
>>> Hello, my name is Monty
>>> What can I do for you?
list
>>> Nothing to list
>>> What can I do for you?
add read book
>>> What can I do for you?
list
>>> List of items:
1. read book
>>> What can I do for you?
add return book
>>> What can I do for you?
list
>>> List of items:
1. read book
2. return book
>>> What can I do for you?
exit
>>> Are you sure? y/n
y
>>> Bye!
You can use the list slicing syntax to extract a portion of a string.
| → |
|
The above technique can be used to extract the item description from an add
command.
Level 5 Mark Tasks as Done
Enhance the Monty Level 4 code in the following ways:
- Add a
done
command so that the user can mark a task as done. e.g.,done 2
marks the task at index 2 as done. - Show appropriate error messages if the user gives an invalid index for the
done
command - Optionally, implement an
undone
(orunmark
) command to change the status of a task back to not done.
A sample output is given below.
>>> Hello, my name is Monty
>>> What can I do for you?
add borrow book
>>> What can I do for you?
add read book
>>> What can I do for you?
add return book
>>> What can I do for you?
list
>>> List of items:
[ ] 1. borrow book
[ ] 2. read book
[ ] 3. return book
>>> What can I do for you?
done 1
>>> What can I do for you?
list
>>> List of items:
[X] 1. borrow book
[ ] 2. read book
[ ] 3. return book
>>> What can I do for you?
done abc
>>> SORRY, I could not perform that command. Problem: abc is not a number
>>> What can I do for you?
done 5
>>> SORRY, I could not perform that command. Problem: No item at index 5
>>> What can I do for you?
done 0
>>> SORRY, I could not perform that command. Problem: Index must be greater than 0
>>> What can I do for you?
garbage
>>> SORRY, I could not perform that command. Problem: Command not recognized
>>> What can I do for you?
exit
>>> Are you sure? y/n
y
>>> Bye!
Each task has two data values: the description and the 'done' status. You can use a list to hold these two data items. That means your list of tasks will be a list containing lists. Example:
tasks = []
tasks.append(['read book', False])
print('Description of the first task:', tasks[0][0])
if tasks[0][1]:
print('X')
else:
print('-')
You can use exceptions to identify and handle errors in the command.
def main():
print_greeting()
while True:
try:
command = read_command()
execute_command(command)
except Exception as e:
print('>>> SORRY, I could not perform that command. Problem:', e)
Level 6 Give Help
Enhance the Monty Level 5 code in the following ways:
- Add a
help
command so that the user can view how to use the app. - Improve the formatting of the text displayed to the user to make the user experience nicer.
A sample output is given below.
*******************************************************************************************
* __ __ _ _ __ __ _ *
* \ \ / / | | | | | \/ | | | *
* \ \ /\ / /__| | ___ ___ _ __ ___ ___ | |_ ___ | \ / | ___ _ __ | |_ _ _ *
* \ \/ \/ / _ \ |/ __/ _ \| '_ ' _ \ / _ \ | __/ _ \ | |\/| |/ _ \| '_ \| __| | | | *
* \ /\ / __/ | (_| (_) | | | | | | __/ | || (_) | | | | | (_) | | | | |_| |_| | *
* \/ \/ \___|_|\___\___/|_| |_| |_|\___| \__\___/ |_| |_|\___/|_| |_|\__|\__, | *
* __/ | *
* |___/ *
*******************************************************************************************
>>> What can I do for you?
help
>>> I'm glad you asked. Here it is:
==================================================
Monty can understand the following commands:
add DESCRIPTION
Adds a task to the list
Example: add read book
done INDEX
Marks the task at INDEX as 'done'
Example: done 1
exit
Exits the application
help
Shows the help information
list
Lists the tasks in the list
--------------------------------------------------
>>> What can I do for you?
add read book
>>> Task added to the list
>>> What can I do for you?
add return book
>>> Task added to the list
>>> What can I do for you?
done 1
>>> Congrats on completing a task! :-)
>>> What can I do for you?
list
>>> Here is the list of tasks:
==================================================
STATUS | INDEX | DESCRIPTION
--------------------------------------------------
X | 1 | read book
- | 2 | return book
--------------------------------------------------
>>> What can I do for you?
You can use triple quotes to define a long string such as the help text.
help_text ='''
long text
more text
'''
You can generate ASCII art using online resources such as http://patorjk.com/software/taag
Level 7 Save Tasks to Disk
Enhance the Monty Level 6 code in the following ways:
- Monty saves tasks into a csv file, and loads data from the same file at the start.
- Add a
delete
command that can delete a task at a specific index.
A sample output is given below. Note the following:
- Monty is able to show at the very start the three tasks loaded from the file.
- When item 2 is deleted, the item previously at index 3 moves to position 2.
*******************************************************************************************
* __ __ _ _ __ __ _ *
* \ \ / / | | | | | \/ | | | *
* \ \ /\ / /__| | ___ ___ _ __ ___ ___ | |_ ___ | \ / | ___ _ __ | |_ _ _ *
* \ \/ \/ / _ \ |/ __/ _ \| '_ ' _ \ / _ \ | __/ _ \ | |\/| |/ _ \| '_ \| __| | | | *
* \ /\ / __/ | (_| (_) | | | | | | __/ | || (_) | | | | | (_) | | | | |_| |_| | *
* \/ \/ \___|_|\___\___/|_| |_| |_|\___| \__\___/ |_| |_|\___/|_| |_|\__|\__, | *
* __/ | *
* |___/ *
*******************************************************************************************
>>> What can I do for you?
list
>>> Here is the list of tasks:
==================================================
STATUS | INDEX | DESCRIPTION
--------------------------------------------------
X | 1 | borrow book
- | 2 | read book
- | 3 | return book
--------------------------------------------------
>>> What can I do for you?
delete 2
>>> Task deleted from the list
>>> What can I do for you?
list
>>> Here is the list of tasks:
==================================================
STATUS | INDEX | DESCRIPTION
--------------------------------------------------
X | 1 | borrow book
- | 2 | return book
--------------------------------------------------
here are some tips:
The filename can be specified in the code. e.g.,
DATA_FILE = 'monty7.csv'
The following statement will create an empty
data.csv
file if the file doesn't exist, but will keep the file as it is if it already exists (it simply opens the file in append mode -- which creates the file it if it doesn't exist -- and close it right after).open('data.csv', 'a').close()
The format of the file is up to you. Here is an example:
borrow book,done read book,pending return book,pending
The program can load the tasks from the file at the beginning. It can save the data after each command. For example, as follows:
items = [] DATA_FILE = 'monty7.csv' def main(): create_file_if_missing(DATA_FILE) load_data(DATA_FILE) # load task data from the file print_greeting() while True: try: command = read_command() execute_command(command) save_data(DATA_FILE, items) # save all tasks in the file except Exception as e: print('>>> SORRY, I could not perform that command. Problem:', e) main()
Given below are some more features you can consider adding at this point (it is optional to add them to Monty 7):
- Remove the need for the user to confirm before exiting Monty. As data are saved to a file, such a confirmation is no longer necessary because an accidental exit will not cause any permanent damage.
>>> What can I do for you? exit >>> Bye!
- Add a
pending
command (other possible names:undone
orunmark
) that can mark a task as not-done-yet (i.e., the opposite of thedone
command), if you haven't done that already.>>> What can I do for you? list >>> Here is the list of tasks: ================================================== STATUS | INDEX | DESCRIPTION -------------------------------------------------- X | 1 | borrow book - | 2 | read book - | 3 | return book -------------------------------------------------- >>> What can I do for you? pending 1 >>> OK, I have marked that item as pending >>> What can I do for you? list >>> Here is the list of tasks: ================================================== STATUS | INDEX | DESCRIPTION -------------------------------------------------- - | 1 | borrow book - | 2 | read book - | 3 | return book -------------------------------------------------- >>> What can I do for you?
- Make commands case insensitive and immune to extra leading/trailing spaces. For example, all these commands should work the same way.
add read book ADD read book Add read book add read book add read book
Level 8 Support Deadlines
Enhance the Monty Level 7 code to add support for keeping track of deadlines as well as regular todo tasks
Previous behavior:
add buy book
: adds a taskbuy book
Proposed change - replace the above command with the following two:
todo read book
: adds a todo taskread book
deadline return book by: May 3rd
adds a deadlinereturn book
which is to be done byMay 3d
.
Note:by:
is a keyword. Anything that comes after it is considered a description of the deadline.
A sample output is given below.
>>> What can I do for you?
list
>>> Here is the list of tasks:
============================================================
STATUS | INDEX | DESCRIPTION | DEADLINE
------------------------------------------------------------
X | 1 | borrow book | -
- | 2 | read book | -
- | 3 | return book | Monday
------------------------------------------------------------
>>> What can I do for you?
todo watch movie
>>> What can I do for you?
deadline submit assignment by: end of May
>>> What can I do for you?
list
>>> Here is the list of tasks:
============================================================
STATUS | INDEX | DESCRIPTION | DEADLINE
------------------------------------------------------------
X | 1 | borrow book | -
- | 2 | read book | -
- | 3 | return book | Monday
- | 4 | watch movie | -
- | 5 | submit assignment | end of May
------------------------------------------------------------
>>> What can I do for you?
here are some tips:
- One option is to use a list of dictionary objects to store the tasks/deadlines. Given below is an example of such a data structure. In that data structure,
T
andD
is used to indicate todo items and deadline items, respectively.[ {'type': 'T', 'description': 'borrow book', 'is_done': True}, {'type': 'T', 'description': 'read book', 'is_done': False}, {'type': 'D', 'description': 'return book', 'is_done': False, 'by': 'Monday'} ]
- The format of the csv file needs to be updated to store extra values too. Here is an example:
T,borrow book,done T,read book,pending D,return book,pending,Monday
Level 9 Use Classes
Enhance the Monty Level 8 code to use classes ToDo
and Deadline
(i.e., you need to define these two classes) to represent todo tasks and deadlines, respectively.
This means you no longer needs to use dict
objects to represent todo/deadline tasks. That is, your tasks can be kept as a list
of ToDo
and Deadline
objects, instead of a list
of dict
objects.
A shorthand for if-else
def get_as_word(status):
if status == 1:
return 'Yes'
else:
return 'No'
The same can be written using the following shorthand for if-else
:
def get_as_word(status):
return 'Yes' if status == 1 else 'No'
A few more examples:
status_as_word = 'Yes' if status == 1 else 'No'
print('Yes') if status == 1 else print('No')
print('Yes' if status == 1 else 'No')
Note that this shorthand requires both the if
part and the else
part.
Level 10 Use More Classes
Enhance the Monty Level 9 code to extract the following classes:
UserInterface
: an object of this class can be used to handle reading input from the user and showing output back to the user.StorageManager
: an object of this class can be used to read data from the data file and write data back to the data file.TaskManager
: an object of this class will hold the list ofTask
/Deadline
objects and will execute commands.
Optional feature to consider: add a progress
command that shows how many tasks/deadlines were marked as done during a session so far. Here is an example output:
>>> What can I do for you?
list
>>> Here is the list of tasks:
============================================================
STATUS | INDEX | DESCRIPTION | DEADLINE
------------------------------------------------------------
X | 1 | borrow book | -
- | 2 | read book | -
- | 3 | return book | Monday
------------------------------------------------------------
>>> What can I do for you?
progress
>>> Progress for this session: todos 0 deadlines 0
>>> What can I do for you?
done 2
>>> Congrats on completing a task! :-)
>>> What can I do for you?
progress
>>> Progress for this session: todos 1 deadlines 0
>>> What can I do for you?
done 3
>>> Congrats on completing a task! :-)
>>> What can I do for you?
progress
>>> Progress for this session: todos 1 deadlines 1
>>> What can I do for you?
pending 2
>>> OK, I have marked that item as pending
>>> What can I do for you?
progress
>>> Progress for this session: todos 0 deadlines 1
>>> What can I do for you?
list
>>> Here is the list of tasks:
============================================================
STATUS | INDEX | DESCRIPTION | DEADLINE
------------------------------------------------------------
X | 1 | borrow book | -
- | 2 | read book | -
X | 3 | return book | Monday
------------------------------------------------------------
>>> What can I do for you?
Level 11 Use Multiple Code Files
Enhance the Monty Level 10 code to divide the source code into multiple files (e.g., todo.py
, deadline.py
, etc.).
Level 12 Use Inheritance
Enhance the Monty Level 11 code to make the Deadline
class inherit from the ToDo
class:
Level 13 Add Unit Tests
Add some unit tests to Monty Level 12 code.
Minimum requirement: 2 unit tests, testing two functions/methods
Level 14 Add a GUI
Enhance the Monty Level 13 code to integrate it with the skeletal GUI given below.
Notes about the given version of the GUI:
- The GUI shows the list of tasks all the time.
- It initializes with some dummy data.
- It only supports an
add
command and ahelp
command.
Here are some screenshots of the GUI:
Extensions: Category B
Provide a way for the user to track events (i.e., things with a start time and an end time such as a meeting), in addition to todo and deadline tasks.
Support the managing of tasks that need to be done after a specific time/task e.g., return book after the exam is over.
Provide support for managing tasks that need to be done within a certain period e.g., collect certificate between Jan 15 and 25th.
Provide support for managing tasks that takes a fixed amount of time but does not have a fixed start/end time e.g., reading the sales report (needs 2 hours).
Extensions: Category C
Add the ability to recognize and deal with duplicate items. e.g., the same task added multiple times.
Provide more flexibility with the data source e.g., the ability for the user to specify which file to use as the data source.
Make Monty understand the meaning of dates/times attached to a task. e.g., sort deadlines based on their dates/times
The ability to sort items e.g., sort deadlines chronologically.
Provide the ability to find tasks based on a search criterion e.g., find items that contains the provided keyword in their description.
Support a way to easily edit details of items e.g., change the deadline date without changing anything else.
Minimal: the ability to update an existing item without having to delete it first
Other ideas:
- the ability to clone items (to easily create new items based on existing items)
Provide a way to attach priorities to items e.g., mark an item as a high
priority (or priority level 1
).
Provide a way to archive items so that the user can remove items from the app but still keep a record of them somewhere e.g., archive all tasks in the list into a file so that the user can start over with a clean slate.
Provide a way to perform tasks on multiple items e.g., delete some specific items in one go.
Provide a way to leverage statistics about the items managed by the App e.g., show the number of tasks that have been completed in the past week.
Make the command syntax more flexible.
Minimal: provide shorter aliases for keywords e.g., t
can be shorter alias for todo
.
Other ideas:
- Allow users to define their own aliases
- Remove the need for the parts of a command to be in a specific order
Extensions: Category D
Support managing info about small snippets of textual information the user wants to record e.g., one's own waist size, a name of a movie that the user wants to remember
Support managing info about expenses e.g., the amounts spent on food, books, transport, etc.
Support keeping records of loans given/taken e.g., money lent/owed to colleagues/friends
Support recording info about places e.g., info about restaurants visited, for future reference
Provide the ability to learn/memorize thingse.g., learn vocabulary, answers to questions
Support managing info about clients e.g., for an insurance agent to keep track of clients