# Tasty Lab Hints Lab notes on getting going thru the early parts of **Tasty**. ## Hint 0 Remember start _very small_ and build outward. The class/program already works, run it and see. ## Hint 1 ### Modeling a series of tasks If you haven't already decided how to start... This is the `tasks` variable. ``` Tasty class. :param tasks: the user tasks ``` (each of those `:param` things are probably instance variables (eventually)) The most important thing is the tasks you're tracking. It tracks all the tasks, things that get added and removed. What are the two things you want to track about each task? - the name/title of the Task _Buy Milk_ - status of Task _complete_ or _not yet_ That means you track `(task_name, task_status)`. so in Python, if you have a pair things that need to be tracked together, a good possibility is a Dictionary. ```python tasks = { "task1": "not yet", "task2": "completed" } ``` Imagine some code to get a `tasks` dict with a few tasks in it: ```python tasks = {} # creates an empty dictionary tasks['Buy Milk'] = 'not yet' tasks['Start Lab'] = 'completed' tasks['Finish Lab'] = 'not yet' # to test if something is done? task_name = 'Buy Milk' if tasks[task_name] == 'not yet': # task is unfinished pass else: # task is 'complete' pass # to list all the tasks for task_name, task_status in tasks.items(): print("- ", task_name, task_status) ``` ## Hint 2 ### Adding code to the class As we have thought about this a bit, we hit on the idea that a dictionary might work well: ```python tasks = {} # creates an empty dictionary # add a task to the empty tasks variable tasks['Buy Milk'] = 'not yet' # to list all the tasks for task_name, task_status in tasks.items(): print("- ", task_name, task_status) ``` Paste this block of code into the python interpreter and observe what happens. Read the code, does it make sense how we are using the task_name as the _key_ and the task_status as the _value_? Add another task, and print out the tasks dict again. ```python tasks = {} # creates an empty dictionary # add a task to the empty tasks variable tasks['Walk Dog'] = 'not yet' # to list all the tasks for task_name, task_status in tasks.items(): print("- ", task_name, task_status) ``` We could put that loop into a function: ```python def print_tasks(tasks): for task_name, task_status in tasks.items(): print("- ", task_name, task_status) ``` We need to think about how to put these ideas _into the class Tasty_. We might... ```python class Tasty: def __init__(self): self.tasks = {} def add_task(self, task_name): """ Add a new task to the user tasks. """ if task_name not in self.tasks: self.tasks[task_name] = "not yet" else: print("Task already added.") def display_tasks(self): for task_name, task_status in self.tasks.items(): print("- ", task_name, task_status) ``` See how those things went from just trial code to _methods_ in the class? Yes, and... the `self` variable is how you know that thing is a _method_ and not just a _function_. That's an example of how I might use the rough class. ## Hint 3 ### How to handle more complex input Student Asks: _I'm struggling to see where we take the user input for the task name._ If we have `command = input('Tasty> ')` So the `command` variable will have `exit` or whatever, after the user types something in and taps , right? And the `new ` command/line when entered, will actually look like `new Buy Milk` when user types the command to be processed by the program. so take the string inputted and split it into a list of strings, breaking each string on the character. ```python txt = "command typed by user" x = txt.split() print(x) # x will be a list: ['command', 'typed', 'by', 'user'] ``` If we want to extend this to make two new strings: - command (the first string, perhaps "new") - and rest (the rest of the split strings glued back together with spaces in between...) ```python txt = "new Buy Milk" t = txt.split() print(t) # t will be: ["new", "Buy", "Milk"] command = t[0] # command will be "new" rest = t[1:] # carefully: Slice the array, from t[1] to end of list # rest will be ["Buy", "Milk"] rest = " ".join(rest) # now rest will be "Buy Milk" print(command, ' - ', rest) ``` and after hacking a bit in the python interpreter, you may end up with ```python line = input(prompt) #print(line) while not line: line = input(prompt) # this causes input to wait until # line is not an empty string words = line.split() #print(words) command = words[0] #print(command) rest = words[1:] rest = " ".join(rest) #print(rest) ``` and maybe the right thing to do is to change the `command = input("Tasty> ")` to something like `command, rest = tasty.prompt_user("Tasty> ")` then all the "words" after the "new" will be what you pull together to name the task? so `prompt-user(self, prompt)` becomes a method which asks for the user's input (using input(prompt)) ```python def prompt_user(self, prompt): inp = input(prompt) # insert the stuff up there about how to # split words into a list and process into two parts. ``` ## Hint 4 ### Saving/Loading to a JSON file JSON is just a structured text format for data. Very Handy. ## saving? a Hint ```python with open("saved_data.json", "w") as fp: json.dump(self.tasks,fp) ``` ## loading? a Hint ```python with open(filename) as json_file: self.tasks = json.load(json_file) ``` And you should embed these two ideas into a method. When you get to the point where you are puzzling over how to save/load multiple dicts (say, _tasks_, _trash_, and _important_), you might consider putting all this in another dictionary and just saving that. You still have to _load_ the one dict and then split it into three for use by the methods. This should be done in _load tasks_ method. The data structure might be created by ```python dict_to_save = { "tasks": self.tasks, "trash": self.trash, "impt": self.important } ``` You can now _json.dump_ the `dict_to_save` to a file. Loading it will be the opposite, you end up with one `loaded_dicts` which have to be split like: `self.tasks = loaded_dicts['tasks']` Easy Peasy