def test_disordered_test(self): garden = Garden("VCRRGVRG\nRVGCCGCV", students="Samantha Patricia Xander Roger".split()) self.assertEqual("Violets Clover Radishes Violets".split(), garden.plants("Patricia")) self.assertEqual("Radishes Grass Clover Violets".split(), garden.plants("Xander"))
def test_full_garden(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") self.assertEqual("Violets Radishes Violets Radishes".split(), garden.plants("Alice")) self.assertEqual("Clover Grass Clover Clover".split(), garden.plants("Bob")) self.assertEqual("Grass Clover Clover Grass".split(), garden.plants("Kincaid")) self.assertEqual("Grass Violets Clover Violets".split(), garden.plants("Larry"))
def xtest_full_garden(self): garden = Garden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV") self.assertEqual("Violets Radishes Violets Radishes".split(), garden.plants("Alice")) self.assertEqual("Clover Grass Clover Clover".split(), garden.plants("Bob")) self.assertEqual("Grass Clover Clover Grass".split(), garden.plants("Kincaid")) self.assertEqual("Grass Violets Clover Violets".split(), garden.plants("Larry"))
def run_event_loop(logger, gardens, garden, window): """ Display and interact with the main window and subwindows using an event loop. Create, select, display, update, and remove gardens and the creatures, plants, and tasks they contain. Save the gardens dict as gardens.pickle. If a fatal exception occurs, log it in gardenlife.log. """ # Keeps track of whether any changes have been made since the gardens dict was saved gardens_changed = False try: while True: event, values = window.read() # print(event, values) ##################### Menu & Window Closure Events ##################### # See if user wants to quit or attempted to close the window if event in ("Exit", sg.WINDOW_CLOSE_ATTEMPTED_EVENT): if gardens_changed: subwindows.unsaved_changes_window(window, gardens) else: break elif event == "Save": with open("gardens.pickle", "wb") as file: pickle.dump(gardens, file) gardens_changed = False elif event == "About...": popups.about() elif event == "Open web tutorial": webbrowser.open("https://github.com/jonboland/gardenlife/blob/master/README.rst") ################ Creature, Plant, & Task Summary Events ################ elif event == "VIEW ALL CREATURES": subwindows.view_creatures_window(window, garden) elif event == "VIEW ALL PLANTS": subwindows.view_plants_window(window, garden) elif event == "VIEW EDIBLE PLANTS": subwindows.view_plants_window(window, garden, attr="edible", title="Edible ") elif event == "VIEW ALL TASKS": subwindows.view_tasks_window(window, garden) ######################### Manage Garden Events ######################### elif event == "GARDEN CREATE/UPDATE": # Validate garden name and ownership info g_name = values["-GARDEN NAME-"].strip() g_owners = values["-OWNER NAMES-"].strip() g_since = values["-OWNED SINCE-"].strip() if not g_name: popups.invalid_name("garden name") continue if not g_owners: popups.invalid_name("owner names") continue try: event_funcs.check_date_validity(g_since) except ValueError: popups.invalid_date(field="owned since", date=g_since) # If there are no validation errors, create/update the garden else: cu_garden = Garden( g_name, values["-LOCATION-"], values["-SIZE-"], g_since, g_owners.split() ) # If garden already exists add all existing items to the updated version garden_instance = gardens.get(g_name) if garden_instance: cu_garden.creatures = garden_instance.creatures cu_garden.plants = garden_instance.plants cu_garden.tasks = garden_instance.tasks # Add created/updated garden to gardens dict. Overwrite if already exists gardens[g_name] = cu_garden # Update dropdowns and clear field values and links event_funcs.update_garden_dropdown(window, gardens) event_funcs.clear_all_item_dropdowns(window) event_funcs.clear_all_values_and_links(window, garden) gardens_changed = True elif event == "GARDEN REMOVE": if not values["-SELECT GARDEN-"]: continue g_confirmation = popups.remove_confirmation(garden.name, "garden") if g_confirmation == "OK": del gardens[values["-GARDEN NAME-"]] event_funcs.update_garden_dropdown(window, gardens) event_funcs.clear_all_item_dropdowns(window) event_funcs.clear_all_values_and_links(window, garden) gardens_changed = True elif event == "-SELECT GARDEN-": # A garden is selected from the dropdown # Load the selected garden garden = gardens[values["-SELECT GARDEN-"]] # If the default garden (blank row) is selected, clear fields if not values["-SELECT GARDEN-"]: event_funcs.clear_garden_values(window) event_funcs.clear_summary_values(window) # Otherwise, update the fields with the selected garden's values else: window["-GARDEN NAME-"].update(garden.name) window["-LOCATION-"].update(garden.location) window["-SIZE-"].update(garden.size) window["-OWNER NAMES-"].update(" ".join(garden.owners)) window["-OWNED SINCE-"].update(garden.since) # And update the associated summary fields window["-SUMMARY GARDEN NAME-"].update(garden.name) window["-SUMMARY LOCATION-"].update(garden.location) window["-SUMMARY SIZE-"].update(garden.garden_size()) window["-SUMMARY OWNED BY-"].update(garden.ownership()) window["-SUMMARY OWNED FOR-"].update(garden.ownership_length()) window["-SUMMARY TOTAL CREATURES-"].update(len(garden.creatures)) window["-SUMMARY TOTAL PLANTS-"].update(len(garden.plants)) event_funcs.update_task_summaries(window, garden) # Then update the item dropdowns and clear item field values and links event_funcs.update_all_item_dropdowns(window, garden) event_funcs.clear_all_item_values_and_links(window, garden) ####################### Manage Creatures Events ######################## elif event == "CREATURE CREATE/UPDATE": # Validate garden name, creature name and appeared date c_name = values["-CREATURE NAME-"].strip() c_appeared = values["-CREATURE APPEARED DATE-"].strip() # Check that a garden has been selected if not values["-SELECT GARDEN-"]: popups.garden_not_selected("creature") continue if not c_name: popups.invalid_name("creature name") continue try: if c_appeared: event_funcs.check_date_validity(c_appeared) except ValueError: popups.invalid_date(field="appeared date", date=c_appeared) else: creature = Creature( name=c_name, org_type=values["-CREATURE TYPE-"], appeared=c_appeared, notes=values["-CREATURE NOTES-"], impact=values["-CREATURE IMPACT SLIDER-"], prevalence=values["-CREATURE PREVALENCE SLIDER-"], trend=values["-CREATURE TREND SLIDER-"], ) if values["-CREATURE STATUS-"] == "Archived": creature.status.archive() garden.add_item("creatures", creature) event_funcs.update_creature_dropdowns(window, garden) event_funcs.clear_creature_values(window) # Clear the task fields and organism links event_funcs.clear_task_values(window) event_funcs.clear_organism_links(window, garden) # Update the associated summary field window["-SUMMARY TOTAL CREATURES-"].update(len(garden.creatures)) gardens_changed = True elif event == "CREATURE REMOVE": if not values["-CREATURE NAME-"] in garden.creatures: popups.item_not_created("creature") continue c_confirmation = popups.remove_confirmation(values["-CREATURE NAME-"], "creature") if c_confirmation == "OK": garden.remove_item("creatures", values["-CREATURE NAME-"]) event_funcs.update_creature_dropdowns(window, garden) event_funcs.clear_creature_values(window) event_funcs.clear_task_values(window) event_funcs.clear_organism_links(window, garden) window["-SUMMARY TOTAL CREATURES-"].update(len(garden.creatures)) gardens_changed = True elif event == "-CREATURE NAME-": if not values["-CREATURE NAME-"]: event_funcs.clear_creature_values(window) continue # If a creature is selected populate the relevant fields with its values creature_instance = garden.creatures.get(values["-CREATURE NAME-"]) window["-CREATURE NAME-"].update(creature_instance.name) window["-CREATURE TYPE-"].update(creature_instance.org_type) window["-CREATURE APPEARED DATE-"].update(creature_instance.appeared) window["-CREATURE STATUS-"].update(creature_instance.status.get()) window["-CREATURE NOTES-"].update(creature_instance.notes) window["-CREATURE IMPACT SLIDER-"].update(creature_instance.impact) window["-CREATURE PREVALENCE SLIDER-"].update(creature_instance.prevalence) window["-CREATURE TREND SLIDER-"].update(creature_instance.trend) ######################### Manage Plant Events ########################## elif event == "PLANT CREATE/UPDATE": # Validate plant name and planted date p_name = values["-PLANT NAME-"].strip() p_planted = values["-PLANT PLANTED DATE-"].strip() # Check that a garden has been selected if not values["-SELECT GARDEN-"]: popups.garden_not_selected("plant") continue if not p_name: popups.invalid_name("plant name") continue try: if p_planted: event_funcs.check_date_validity(p_planted) except ValueError: popups.invalid_date(field="planted date", date=p_planted) else: plant = Plant( name=p_name, org_type=values["-PLANT TYPE-"], planted=p_planted, edible=values["-PLANT EDIBLE-"], notes=values["-PLANT NOTES-"], impact=values["-PLANT IMPACT SLIDER-"], prevalence=values["-PLANT PREVALENCE SLIDER-"], trend=values["-PLANT TREND SLIDER-"], ) if values["-PLANT STATUS-"] == "Archived": plant.status.archive() garden.add_item("plants", plant) event_funcs.update_plant_dropdowns(window, garden) event_funcs.clear_plant_values(window) event_funcs.clear_task_values(window) event_funcs.clear_organism_links(window, garden) window["-SUMMARY TOTAL PLANTS-"].update(len(garden.plants)) gardens_changed = True elif event == "PLANT REMOVE": if not values["-PLANT NAME-"] in garden.plants: popups.item_not_created("plant") continue p_confirmation = popups.remove_confirmation(values["-PLANT NAME-"], "plant") if p_confirmation == "OK": garden.remove_item("plants", values["-PLANT NAME-"]) event_funcs.update_plant_dropdowns(window, garden) event_funcs.clear_plant_values(window) event_funcs.clear_task_values(window) event_funcs.clear_organism_links(window, garden) window["-SUMMARY TOTAL PLANTS-"].update(len(garden.plants)) gardens_changed = True elif event == "-PLANT NAME-": if not values["-PLANT NAME-"]: event_funcs.clear_plant_values(window) continue # If a plant is selected populate the relevant fields with its values plant_instance = garden.plants.get(values["-PLANT NAME-"]) window["-PLANT NAME-"].update(plant_instance.name) window["-PLANT TYPE-"].update(plant_instance.org_type) window["-PLANT PLANTED DATE-"].update(plant_instance.planted) window["-PLANT STATUS-"].update(plant_instance.status.get()) window["-PLANT EDIBLE-"].update(plant_instance.edible) window["-PLANT NOTES-"].update(plant_instance.notes) window["-PLANT IMPACT SLIDER-"].update(plant_instance.impact) window["-PLANT PREVALENCE SLIDER-"].update(plant_instance.prevalence) window["-PLANT TREND SLIDER-"].update(plant_instance.trend) ########################## Manage Task Events ########################## elif event == "TASK CREATE/UPDATE": # Check that a garden has been selected if not values["-SELECT GARDEN-"]: popups.garden_not_selected("task") continue # Strip and validate task name and set schedule values # NB: Frequency is not validated because it's a readonly dropdown t_name = values["-TASK NAME-"].strip() start_date = values["-TASK START-"].strip() count = values["-TASK COUNT-"].strip() bymonth = values["-TASK BY MONTH-"].strip() interval = values["-TASK INTERVAL-"].strip() if not t_name: popups.invalid_name("task name") continue try: if start_date: event_funcs.check_date_validity(start_date) except ValueError: popups.invalid_date(field="first due", date=start_date) continue if count and not count.isdigit(): popups.invalid_digit(field="count", digit=count) elif bymonth and any(month not in MONTHS for month in bymonth.split(" ")): popups.invalid_bymonth(bymonth) elif interval and not interval.isdigit(): popups.invalid_digit(field="interval", digit=interval) # If there are no validation errors, create/update the task else: task = Task( name=t_name, assignee=values["-TASK ASSIGNEE-"], length=values["-TASK LENGTH-"], linked_creatures=values["-TASK LINKED CREATURES-"], linked_plants=values["-TASK LINKED PLANTS-"], description=values["-TASK NOTES-"], ) task.set_schedule( start_date=start_date, freq=values["-TASK FREQUENCY-"], count=count, bymonth=bymonth, interval=interval, ) # Handle rare situation where provided shedule doesn't produce any due dates if not task.schedule: popups.no_due_dates() continue if values["-TASK STATUS-"] == "Archived": task.status.archive() # If the task already exists add any pre-existing completed dates to it task_instance = garden.tasks.get(values["-TASK NAME-"]) if task_instance: task.completed_dates = task_instance.completed_dates # Add the task to the garden, overwriting the old version if it already exists garden.add_item("tasks", task) event_funcs.update_task_dropdown(window, garden) # Clear the task fields and variable once the task has been added to the garden event_funcs.clear_task_values(window) event_funcs.clear_organism_links(window, garden) task = None # Update the task numbers shown on the summary tab event_funcs.update_task_summaries(window, garden) gardens_changed = True elif event == "TASK REMOVE": if not values["-TASK NAME-"] in garden.tasks: popups.item_not_created("task") continue t_confirmation = popups.remove_confirmation(values["-TASK NAME-"], "task") if t_confirmation == "OK": garden.remove_item("tasks", values["-TASK NAME-"]) event_funcs.update_task_dropdown(window, garden) event_funcs.clear_task_values(window) task = None event_funcs.clear_organism_links(window, garden) event_funcs.update_task_summaries(window, garden) gardens_changed = True elif event == "ADD PROGRESS": # Check the task variable exists and has been assigned to a task if "task" in locals() and task: # Open the progress subwindow and set gardens_changed flag if progress added if subwindows.add_progress_window(window, task): gardens_changed = True else: popups.item_not_created("task", "progress can be added") elif event == "-TASK NAME-": if not values["-TASK NAME-"]: event_funcs.clear_task_values(window) event_funcs.clear_organism_links(window, garden) task = None continue # If a task is selected populate the relevant fields with its values task_instance = garden.tasks[values["-TASK NAME-"]] window["-TASK PROGRESS-"].update(task_instance.get_current_progress()) window["-TASK NEXT DUE-"].update(task_instance.get_next_due_date()) window["-TASK ASSIGNEE-"].update(task_instance.assignee) window["-TASK LENGTH-"].update(task_instance.length) window["-TASK LINKED CREATURES-"].set_value(task_instance.linked_creatures) window["-TASK LINKED PLANTS-"].set_value(task_instance.linked_plants) window["-TASK STATUS-"].update(task_instance.status.get()) window["-TASK NOTES-"].update(task_instance.description) window["-TASK START-"].update(task_instance.raw_schedule["start date"]) window["-TASK FREQUENCY-"].update(task_instance.raw_schedule["freq"]) window["-TASK COUNT-"].update(task_instance.raw_schedule["count"]) window["-TASK BY MONTH-"].update(task_instance.raw_schedule["bymonth"]) window["-TASK INTERVAL-"].update(task_instance.raw_schedule["interval"]) # Then assign instance to task variable so progress can be added task = task_instance # Handle fatal exceptions gracefully and log them in the gardenlife.log file except Exception as ex: logger.exception("Fatal Error") popups.fatal_error(ex) # Finish up by removing from the screen window.close()
def test_bob_and_charlies_gardens(self): garden = Garden("VVCCGG\nVVCCGG") self.assertEqual(["Clover"] * 4, garden.plants("Bob")) self.assertEqual(["Grass"] * 4, garden.plants("Charlie"))
def xtest_bob_and_charlies_gardens(self): garden = Garden("VVCCGG\nVVCCGG") self.assertEqual(["Clover"] * 4, garden.plants("Bob")) self.assertEqual(["Grass"] * 4, garden.plants("Charlie"))