This site is from a past semester! The current version will be here when the new semester starts.
TE3201 2020
  • Full Timeline
  • Week 1 [Jan 13]
  • Week 2 [Jan 20]
  • Week 3 [Jan 27]
  • Week 4 [Feb 3]
  • Week 5 [Feb 10]
  • Week 6 [Feb 17]
  • Week 7 [Mar 2]
  • Week 8 [Mar 9]
  • Week 9 [Mar 16]
  • Week 10 [Mar 23]
  • Week 11 [Mar 30]
  • Week 12 [Apr 6]
  • Week 13 [Apr 13]
  • SE Textbook
  • Programming Textbook
  • Admin Info
  • Forum
  • Instructors
  • Announcements
  • repl.it link
  • Files (slides, handouts etc.)
  • Project Info
  • The Monty Project

    The Monty Python Comedy Group,
    [credit: Wikipedia]

    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
    --------------------------------------------------
    ✓ | 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 to Level 14 to indicate how the features make 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!
    print('>>> Hello, my name is Monty')
    # ADD MORE CODE HERE

    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!
    print('>>> Hello, my name is Monty')
    while True:
    print('>>> What can I do for you?')
    # ADD MORE CODE HERE
    print('>>> 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()
    import sys

    def is_exit_confirmed():
    print('>>> Are you sure? y/n')
    response = input()
    return response == 'y'


    def execute_command(command):
    if command == '':
    return
    elif command == 'exit':
    if is_exit_confirmed():
    print('>>> Bye!')
    sys.exit()
    # ...


    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.


    s = 'abcdefgh'
    print(s[2:])
    print(s[:5])
     → 

    cdefgh
    abcde

    The above technique can be used to extract the item description from an add command.

    import sys

    items = []


    def print_items():
    if len(items) == 0:
    print('>>> Nothing to list')
    else:
    for i, item in enumerate(items):
    # ...


    def add_item(user_input):
    # ...


    def terminate():
    # ...

    # ...

    def execute_command(command):
    if command == '':
    return
    elif command == 'exit':
    terminate()
    elif command == 'list':
    print_items()
    elif command.startswith('add '):
    add_item(command)
    else:
    print('>>> OOPS! Unknown command')


    def main():
    print_greeting()
    while True:
    command = read_command()
    execute_command(command)


    main()

    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

    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:
    [✓] 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('✓')
    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)

    items = []

    # ...

    def get_item_for_index(index_as_string):
    try:
    index = int(index_as_string.strip())
    except Exception:
    raise ValueError(index_as_string + ' is not a number ')

    if index < 1:
    raise ValueError('Index must be greater than 0 ')

    try:
    return items[index - 1]
    except IndexError:
    raise ValueError('No item at index ' + index_as_string)


    def mark_item_as_done(user_input):
    index_as_string = user_input[5:]
    get_item_for_index(index_as_string)[1] = True


    def execute_command(command):
    if command == '':
    return
    elif command == 'exit':
    terminate()
    elif command == 'list':
    print_items()
    elif command.startswith('add '):
    add_item(command)
    elif command.startswith('done '):
    mark_item_as_done(command)
    else:
    raise Exception('Command not recognized')


    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)


    main()

    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
    --------------------------------------------------
    ✓ | 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

    def print_greeting():
    banner = '''
    *******************************************************************************************
    * __ __ _ _ __ __ _ *
    * \ \ / / | | | | | \/ | | | *
    * \ \ /\ / /__| | ___ ___ _ __ ___ ___ | |_ ___ | \ / | ___ _ __ | |_ _ _ *
    * \ \/ \/ / _ \ |/ __/ _ \| '_ ' _ \ / _ \ | __/ _ \ | |\/| |/ _ \| '_ \| __| | | | *
    * \ /\ / __/ | (_| (_) | | | | | | __/ | || (_) | | | | | (_) | | | | |_| |_| | *
    * \/ \/ \___|_|\___\___/|_| |_| |_|\___| \__\___/ |_| |_|\___/|_| |_|\__|\__, | *
    * __/ | *
    * |___/ *
    *******************************************************************************************
    '''
    print(banner.strip(), '\n')

    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
    --------------------------------------------------
    ✓ | 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
    --------------------------------------------------
    ✓ | 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()

    Here is the code for loading data from the file.

    def load_data(filename):
    data_file = open(filename)
    deliveries_reader = csv.reader(data_file)
    for row in deliveries_reader:
    if not row:
    continue
    add_item_from_csv_line(row)
    data_file.close()


    def add_item_from_csv_line(values):
    status = True if values[1] == 'done' else False
    items.append([values[0], status]) # items is a global variable

    Note that status = True if values[1] == 'done' else False is a shortcut syntax. It is equivalent to the code below:

    if values[1] == 'done':
    status = True
    else:
    status = False

    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 that can mark a task as pending (i.e., the opposite of the done command).
       >>> 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?

      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 task buy book

    Proposed change - replace the above command with the following two:

    • todo read book: adds a todo task read book
    • deadline return book by: May 3rd adds a deadline return book which is to be done by May 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
    ------------------------------------------------------------
    ✓ | 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
    ------------------------------------------------------------
    ✓ | 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 and D 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

    Here is the code adding a todo item:


    command_parts = command.strip().split(' ', 1) # split the command into two: first word and the remainder
    if action == 'todo':
    items.append({'type': 'T', 'description': command_parts[1], 'is_done': False})
    elif ...


    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.

    Here is the code for an example ToDo class:

    class ToDo:

    def __init__(self, description, status):
    self.description = description
    self.is_done = status

    def mark_as_done(self):
    self.is_done = True

    def mark_as_pending(self):
    self.is_done = False

    def as_string(self):
    """ Return the details of todo object as a string"""
    status = '✓' if self.is_done else '✗'
    return status.center(6) + ' ' + ' -'.ljust(13) + self.description

    def as_csv(self):
    """ Return the details of todo object as a list,
    suitable to be stored in a csv file.
    """
    return ['T', self.description, 'done' if self.is_done else 'pending']

    Here are some sample functions that work with ToDo objects:

    def load_data(filename):
    data_file = open(filename)
    deliveries_reader = csv.reader(data_file)
    for row in deliveries_reader:
    if row[0] == 'T':
    items.append(ToDo(row[1], True if row[2] == 'done' else False))
    elif row[0] == 'D':
    items.append(Deadline(row[1], True if row[2] == 'done' else False, row[3]))
    data_file.close()


    def save_data(filename, items_to_save):
    output_file = open(filename, 'w', newline='')
    output_writer = csv.writer(output_file)
    for item in items_to_save:
    output_writer.writerow(item.as_csv())
    output_file.close()


    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 of Task/Deadline objects and will execute commands.

    Here's the main part of the code that illustrates how these objects can work together:

    def main():
    # create a UserInterface object and show the greeting
    ui = UserInterface()
    ui.show_greeting()

    # create a StorageManager object
    storage = StorageManager('data.csv')

    # create a TaskManager object and load data
    task_manager = TaskManager(storage)
    task_manager.load_data()

    while True:
    try:
    command = ui.read_command()
    execute_command(command, task_manager, ui)
    task_manager.save_data()
    except Exception as e:
    ui.display('SORRY, I could not perform that command. Problem:' + str(e))


    main()

    Here's a partial execute_command function:

    def execute_command(command_parts, task_manager, ui):
    if action == 'exit':
    ui.display('Bye!')
    sys.exit()
    elif action == 'list':
    message = task_manager.get_items_as_table()
    ui.display(message)
    elif action == 'todo':
    message = task_manager.add_item(ToDo(command_parts[1], False))
    ui.display(message)
    ...

    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
    ------------------------------------------------------------
    ✓ | 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
    ------------------------------------------------------------
    ✓ | 1 | borrow book | -
    ✗ | 2 | read book | -
    ✓ | 3 | return book | Monday
    ------------------------------------------------------------
    >>> What can I do for you?

    A class-level variable in the ToDo class can be used to track the number of todo tasks marked as done in a session.

    class ToDo:

    progress = 0 # class-level variable

    def __init__(self, description, status):
    self.description = description
    self.is_done = status

    def mark_as_done(self):
    if not self.is_done: # increment progress if needed
    ToDo.progress = ToDo.progress + 1
    self.is_done = True


    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:

    class ToDo:

    TYPE_KEY = 'T'

    def __init__(self, description, status):
    self.description = description
    self.is_done = status

    def mark_as_done(self):
    self.is_done = True

    def mark_as_pending(self):
    self.is_done = False

    def __status_as_icon(self):
    return '✓' if self.is_done else '✗'

    def __str__(self):
    return '(' + self.__status_as_icon() + ') ' + self.description

    # more code ...


    class Deadline (ToDo):

    TYPE_KEY = 'D'

    def __init__(self, description, status, by):
    super().__init__(description, status)
    self.by = by

    def __str__(self):
    return super().__str__() + ' [by: ' + self.by + ']'

    Note:

    • There no need for the Deadline class to implement methods such as mark_as_done because it inherits those methods from the parent class.
    • Note how the Deadline class overrides some methods (e.g., __init__) but uses the parent class method inside the overridden method (e.g., __init__ method of the Deadline class calls the super().__init__() method.

    Level 13 Add Unit Tests

    Add some unit tests to Monty Level 12 code.


    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 a help command.

    Here are some screenshots of the GUI:

    import datetime
    from tkinter import *

    import sys


    class GUI:

    def __init__(self, task_manager):
    self.task_manager = task_manager
    self.window = Tk()
    self.window.geometry('800x700') # set Window size
    self.window.title('Monty') # set Window title

    self.input_box = Entry(self.window) # create an input box
    self.input_box.pack(padx=5, pady=5, fill='x') # make the input box fill the width of the Window
    self.input_box.bind('<Return>', self.command_entered) # bind the command_entered function to the Enter key
    self.input_box.focus() # set focus to the input box

    # add a text area to show the chat history
    self.history_area = Text(self.window, width="50")
    self.history_area.pack(padx=5, pady=5, side=LEFT, fill="y")
    self.output_font = ('Courier New', 12)
    self.history_area.tag_configure('error_format', foreground='red', font=self.output_font)
    self.history_area.tag_configure('success_format', foreground='green', font=self.output_font)
    self.history_area.tag_configure('normal_format', font=self.output_font)

    # add a text area to show the list of tasks
    self.list_area = Text(self.window)
    self.list_area.pack(padx=5, pady=5, side=RIGHT, fill="both")
    self.list_area.tag_configure('normal_format', font=self.output_font)
    self.list_area.tag_configure('pending_format', foreground='red', font=self.output_font)
    self.list_area.tag_configure('done_format', foreground='green', font=self.output_font)

    # show the welcome message and the list of tasks
    self.update_chat_history('start', 'Welcome to Monty!', 'success_format')
    self.update_task_list(self.task_manager.items)

    def update_chat_history(self, command, response, status_format):
    """
    status_format: indicates which color to use for the status message
    can be 'error_format', 'success_format', or 'normal_format'
    """
    current_time = datetime.datetime.now().strftime("%H:%M:%S")
    self.history_area.insert(1.0, '-' * 40 + '\n', 'normal_format')
    self.history_area.insert(1.0, '>>> ' + response + '\n', status_format)
    self.history_area.insert(1.0, 'You said: ' + command + '\n', 'normal_format')
    self.history_area.insert(1.0, current_time + '\n', 'normal_format')

    def update_task_list(self, tasks):
    self.list_area.delete('1.0', END) # clear the list area
    for i, task in enumerate(tasks):
    if task[1]:
    icon = '✓'
    output_format = 'done_format'
    else:
    icon = '✗'
    output_format = 'pending_format'
    self.list_area.insert(END, icon + ' ' + str(i+1) + ' ' + task[0] + '\n', output_format)

    def clear_input_box(self):
    self.input_box.delete(0, END)

    def command_entered(self, event):
    command = None
    try:
    command = self.input_box.get()
    if command.strip().lower() == 'exit':
    sys.exit()
    output = self.task_manager.execute_command(command)
    self.update_chat_history(command, output, 'success_format')
    self.update_task_list(self.task_manager.items)
    self.clear_input_box()
    except Exception as e:
    self.update_chat_history(command, str(e) + '\n' + self.task_manager.get_help(), 'error_format')

    def start(self):
    self.window.mainloop()


    class TaskManager:
    def __init__(self):
    self.items = [
    ['task 1', True],
    ['task 2', False],
    ['task 3', True]
    ]

    def get_help(self):
    return 'help:\n...'

    def execute_command(self, command):
    if command == 'help':
    return self.get_help()
    elif command.startswith('add '):
    self.items.append([command[4:], False])
    return 'task added'
    else:
    raise Exception('Command not recognized')


    GUI(TaskManager()).start()

    Extensions: Category B

    B-Events

    Events

    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.


    B-DoAfterTasks

    'Do after' 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.


    B-DoWithinPeriodTasks

    'Do within a period' task

    Provide support for managing tasks that need to be done within a certain period e.g., collect certificate between Jan 15 and 25th.


    B-FixedDurationTasks

    Unscheduled tasks with a fixed duration

    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

    C-DetectDuplicates

    Deal duplicate items

    Add the ability to recognize and deal with duplicate items. e.g., the same task added multiple times.


    C-FlexibleDataSource

    Flexible data source

    Provide more flexibility with the data source e.g., the ability for the user to specify which file to use as the data source.


    C-DateTimes

    Meaningful date/times

    Make Monty understand the meaning of dates/times attached to a task. e.g., sort deadlines based on their dates/times


    C-Sort

    Sorting items managed by the App

    The ability to sort items e.g., sort deadlines chronologically.

    C-Update

    Easily edit items

    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)

    C-Tagging

    Tagging items

    Provide a way to tag items e.g., tag a task as #fun.

    C-Priority

    Prioritizing items

    Provide a way to attach priorities to items e.g., mark an item as a high priority (or priority level 1).

    C-Archive

    Archiving items

    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.

    C-MassOps

    Mass operations

    Provide a way to perform tasks on multiple items e.g., delete some specific items in one go.

    C-Statistics

    Statistics and insights

    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.

    C-FriendlierSyntax

    Friendlier syntax for commands

    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

    D-Contacts

    Support managing contacts

    Support managing info about contacts e.g., details of friends

    D-Notes

    Support managing notes

    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

    D-Expenses

    Support managing expenses

    Support managing info about expenses e.g., the amounts spent on food, books, transport, etc.

    D-Loans

    Support managing loan records

    Support keeping records of loans given/taken e.g., money lent/owed to colleagues/friends

    D-Places

    Support managing info about places

    Support recording info about places e.g., info about restaurants visited, for future reference

    D-Trivia

    Support managing trivia

    Provide the ability to learn/memorize thingse.g., learn vocabulary, answers to questions

    D-Clients

    Support managing client info

    Support managing info about clients e.g., for an insurance agent to keep track of clients

    D-Merchandise

    Support managing merchandise info

    Support managing info about merchandise e.g., a property agent to keep track of properties, a collector of stamps keep track of items in the collection