Event-Driven Programming

This page lists the milestone requirements for Milestone 5 of the CC 410 Restaurant Project. Read the requirements carefully and discuss any questions with the instructors or TAs.

Purpose

The CC 410 Restaurant Project project for this semester is centered around building a point of sale (POS) system for a fictional restaurant named Starfleet Subs, based in the Star Trek universe.

The fifth milestone involves dealing with the various events generated by the GUI and constructing a list of items that represent an order.

General Requirements

This milestone must follow these professional coding standards:

  • All code must be object-oriented.
    • All executable code must be within a class
      • Python package files such as __init__.py and __main__.py are exempt.
    • Classes must be organized into packages based on common usage.
  • This project must include automation for compilation, unit testing, style checking, documentation generation, and execution.
    • Java: Use Gradle with the application plugin. The project should compile without errors.
    • Python: Use tox configured to use Python 3.6 and a requirements file to install libraries.
  • All code must properly compile or be interpreted.
    • Java: It must compile using Gradle.
    • Python: It must be interpreted using Python 3.6. Where specified, type hints should be included in the code, and all code should pass a strict Mypy type check with low imprecision percentage.
      • Classes in the starfleetsubs.gui package do not require type hints in Python, though you may continue to use them if they are helpful. Any errors from Mypy originating in these classes will be ignored.
  • Where specified, code should contain appropriate unit tests that achieve the specified level of code coverage.
    • Java: Use JUnit 5. You may choose to use Hamcrest for assertions.
    • Python: Use pytest. You may choose to use Hamcrest for assertions.
  • Where specified, code should contain appropriate documentation comments following the language’s style guide.
    • Java: Use javadoc to generate documentation.
    • Python: Use pdoc3 to generate documentation.
  • All code submitted must be free of style errors. We will be using the Google Style Guide for each language.
    • Java: Use Checkstyle 8.38+ and the Google Style Configuration.
      • You may modify the configuration to allow 4 space indentations instead of 2 space indentations.
    • Python: Use Flake8 with the flake8-docstrings and pep8-naming plugins. Code should conform to PEP 8 style with Google style docstrings.
  • Submissions to Canvas should be tagged GitHub releases that are numbered according to Semantic Versioning.

Assignment Requirements

This milestone should include the following new GUI features:

  • Changes to the previous milestone:
    • Add a “Cancel” button to each of the entrée, side, or drink panels.
    • Add a “Delete” button to the SidebarPanel panel.
    • Switch the list box in the SidebarPanel to a tree element (Java JTree or tkinter Treeview). See the associated example project for code you can use for this.

See the updated GUI mockups below for some design ideas.

Once the entire project is working, you should observe the following behavior on the new tree element.

  • When an item is added to the tree element in the SidebarPanel, the following should happen:
    • The item’s string representation should be a top-level node in the tree.
    • Any special instructions for that item should be represented as child nodes of the top-level node.
    • The item should be shown fully expanded by default.
    • The tree element should only allow the user to select one item at a time.

Tip

It may be helpful to maintain a hash map or dictionary in the SidebarPanel class that associates nodes in the GUI tree element with the actual OrderItem instances that they represent.

In addition, the following events should be implemented in the GUI:

  • When the “Save” button in any of the entrée, side, or drink panels is clicked, the following should happen:
    • The item currently represented by the panel should be updated such that each attribute matches the current status of the associated GUI element.
    • The item should be placed into the tree in the SidebarPanel if it is a new item, or the item should be updated if it is being edited.
    • The main panel in MainWindow should be replaced with the OrderScreen (this was part of the previous milestone).
  • When the “Cancel” button in any of the entrée, side, or drink panels is clicked, the following should happen:
    • If an item is being edited, any changes made in the GUI should be discarded (the item should not be changed).
    • The main panel in MainWindow should be replaced with the OrderScreen (this was part of the previous milestone).
  • When the “Edit” button in the SidebarPanel is clicked, the following should happen:
    • The OrderItem that is currently selected should be determined. If the selection is an ingredient of that item, the code should work upwards in the tree to find the OrderItem.
    • The appropriate entrée, side, or drink panel should be loaded into the main panel in MainWindow and populated with the current status of the item (most of this should work from the previous milestone).
    • If the item is saved via the “Save” button, it’s entry in the tree element should be updated without changing the order of the items in the tree.
    • If the changes are cancelled via the “Cancel” button, no changes should be made.
  • When the “Delete” button in the SidebarPanel is clicked, the following should happen:
    • The OrderItem that is currently selected should be determined. If the selection is an ingredient of that item, the code should work upwards in the tree to find the OrderItem.
    • That item should be removed from the tree element and any other relevant data structures in the SidebarPanel class.

Unit tests should be added to the corresponding test package for the following classes:

  • Each entrée panel in starfleetsubs.gui.entrees
  • Each drink panel in starfleetsubs.gui.drinks
  • The single side panel in starfleetsubs.gui.sides.SidePanel

See below for a list of suggested unit tests. You should achieve at or near 100% coverage on these classes. We will not unit test the MainWindow, OrderPanel, or SidebarPanel classes in this milestone.

Warning

Python users: See the section at the bottom of this milestone for updates to the tox.ini file to enable full unit testing via tox.

Finally, the following requirements from the previous milestone are continued:

  • Classes in the starfleetsubs.gui base package do not require unit tests, but all entrée, drink, and side panels require unit tests as outlined above.
  • Classes and unit tests in the starfleetsubs.gui package and sub-packages do not require type hints in Python, though you may continue to use them if they are helpful. Any errors from Mypy originating in these classes will be ignored.
  • Classes and unit tests in the starfleetsubs.gui package and sub-packages do require all appropriate documentation comments, and must be free of style errors.
  • Update the UML Diagram Contained in this project to match the updated structure of the project. There will most likely not be any new classes, but new associations between existing classes.

Time Requirements

Completing this project is estimated to require 3-8 hours.

Tip

A rough estimate for this milestone would be around 3000-3500 lines of new or updated code. It could vary widely based on how you choose to implement the various portions of the GUI. Most of the new code (around 2000-2500 lines) is contained in the unit tests, which are highly redundant. It took me less than an hour to take a working set of unit tests for TheRikerPanel and use that as a template to create the rest of the unit tests. My current model solution contains 849 unit tests, and I was able to achieve 100% code coverage on all GUI OrderItem panels. -Russ

Grading Rubric

This assignment will be graded based on the rubric below:

  • New GUI elements (“Cancel”, “Delete” and tree element): 5%
  • Tree element displays order items correctly: 5%
  • “Save” buttons work properly for all items: 25%
  • “Cancel” buttons work properly for all items: 5%
  • “Edit” button works properly for all items: 25%
  • “Delete” button works properly for all items: 5%
  • Unit Tests: 20%
  • Updated UML Diagram: 10%

The following deductions apply:

  • Any portion of the project which will not compile (Java), pass a strict type check (Python), or execute properly will be given a grade of 0.

This is not an exhaustive list of possible deductions. The instructors will strive to provide reasonable and fair grading, but we can’t predict all possible defects. It is up to the student to ensure that the project is complete and correct before submission.

Submission

Submit this assignment by creating a release on GitHub and uploading the release URL to the assignment on Canvas. You should not submit this Codio project or mark it as complete in Codio, in case you need to come back to it and make changes later.




Updated GUI Sketches

Below are some GUI sketches to help you visualize one possible GUI for this project. You do not have to match this design at all, but this is at least a good starting point that you can reach based on what you know so far.

Main Window

Main Window

Entree Panel

Main Window

Helpful Methods

I found these methods helpful in my solution:

  • MainWindow.addItem(item) - basically a pass-through method that calls the SidebarPanel.addItem(item) method. This method would be accessible to all order item panels since they get a reference to the MainWindow instance.
  • SidebarPanel.addItem(item) - adds a new item to the tree element, or updates the item if it is already contained in the tree.
  • You must check that the two elements are the same instance, not just that they are equal. Otherwise, two items with the same ingredients and condiments will be regarded as the same item, preventing the user from ordering more than one of them. This can be done using the == operator in Java, or the is operator in Python. This means that you won’t be able to use the normal contains() or in method for determining if the item is already in a list - you must iterate through the list directly.
  • SidebarPanel.updateTree(item, node) - handles actually updating the tree. If node is null or not provided, it creates a new one, otherwise it uses the existing node and updates it. It should return the node or that node’s id when complete.

Unit Tests

Entrée Panels

Each entrée panel test class should contain unit tests for the following:

  • testDefaultConstructor() - create the panel without providing an existing element, and assert that it creates a new instance of the correct item.
  • testBadActionCommand() - call the actionPerformed() method with a bad action command, and assert that an exception is not thrown.
  • testBreadComboBox(Bread) - instantiate a panel with an existing item, change the value of the bread combo box in the GUI to the bread value, and fire a “save” action, then verify that the item has the correct bread value.
  • testBreadComboBoxSetCorrectly(Bread) - instantiate a panel with an existing item using the given bread, and assert that the bread combo box is set to the correct value.
  • test<Ingredient>CheckBox() - instantiate a panel with an existing item, change the value of the ingredient check box in the GUI to a value, and fire a “save” action, then verify that the item has the correct value. Do this for both true and false.
  • test<Ingredient>CheckBoxSetCorrectly() - instantiate a panel with an existing item with a given value for ingredient, and assert that the ingredient checkbox is set to the correct value. Do this for both true and false.
  • testCondimentCheckBox(Condiment) - instantiate a panel with an existing item, change the value of the condiment check box in the GUI to a value, and fire a “save” action, then verify that the item has the correct value. Do this for both true and false.
  • testCondimentCheckBoxSetCorrectly(Condiment) - instantiate a panel with an existing item with a given value for condiment, and assert that the condiment checkbox is set to the correct value. Do this for both true and false.
  • testCancelButton() - instantiate a panel with an existing item, change several values in the GUI, and fire a “cancel” action, then assert that the item is unchanged from its previous state.
Side Panels

Each side panel test class should contain unit tests for the following:

  • testDefaultConstructor<Side>() - create the panel by providing an instance of each side class, and test that its item is set to an instance of that class.
  • testBadActionCommand() - call the actionPerformed() method with a bad action command, and assert that an exception is not thrown.
  • testSizeComboBox(Size) - instantiate a panel with an existing item, change the value of the size combo box in the GUI to the size value, and fire a “save” action, then verify that the item has the correct size value.
  • testSizeComboBoxSetCorrectly(Size) - instantiate a panel with an existing item using the given size, and assert that the size combo box is set to the correct value.
  • testCancelButton() - instantiate a panel with an existing item, change several values in the GUI, and fire a “cancel” action, then assert that the item is unchanged from its previous state.
  • testConstructorEmptyItem() - create the panel by passing a null item, and verify that an exception is thrown.
Drink Panels

Each drink panel test class should contain unit tests for the following:

  • testDefaultConstructor() - create the panel without providing an existing element, and assert that it creates a new instance of the correct item.
  • testBadActionCommand() - call the actionPerformed() method with a bad action command, and assert that an exception is not thrown.
  • testSizeComboBox(Size) - instantiate a panel with an existing item, change the value of the size combo box in the GUI to the size value, and fire a “save” action, then verify that the item has the correct size value.
  • testSizeComboBoxSetCorrectly(Size) - instantiate a panel with an existing item using the given size, and assert that the size combo box is set to the correct value.
  • test<Ingredient>CheckBox() - instantiate a panel with an existing item, change the value of the ingredient check box in the GUI to a value, and fire a “save” action, then verify that the item has the correct value. Do this for both true and false.
  • test<Ingredient>CheckBoxSetCorrectly() - instantiate a panel with an existing item with a given value for ingredient, and assert that the ingredient checkbox is set to the correct value. Do this for both true and false.
  • testCancelButton() - instantiate a panel with an existing item, change several values in the GUI, and fire a “cancel” action, then assert that the item is unchanged from its previous state.

Note

To allow proper unit testing, you may need to relax the permissions on several elements inside of your GUI classes. I recommend using package-private in Java, with no modifier - see this document. Then, any unit tests that are in the same package can have access to those members. For Python, switching from double underscore private attributes to single underscore protected attributes is sufficient.

Python tox Updates

I ran into issues with Python not running unit tests in tox properly on this assignment. There are two causes:

  • Because tox runs in a virtual environment by default, it is unable to construct the graphical tkinter elements for testing. This can be resolved by passing through the DISPLAY environment variable.
  • Because pytest is apparently very memory inefficient, the unit tests are killed by Codio once they consume too much memory. To work around this, we’ll run our unit tests in batches.

An updated tox.ini file is given below. I recommend replacing your file with this one:

[tox]
envlist = py36
skipsdist = True

[testenv]
deps = -rrequirements.txt
passenv = DISPLAY
ignore_errors = True
commands = python3 -m mypy -p src --html-report reports/mypy
           python3 -m coverage run --parallel-mode --source src -m pytest test/starfleetsubs/data --html=reports/pytest-data/index.html
           python3 -m coverage run --parallel-mode --source src -m pytest test/starfleetsubs/gui/entrees --html=reports/pytest-entrees/index.html
           python3 -m coverage run --parallel-mode --source src -m pytest test/starfleetsubs/gui/drinks test/starfleetsubs/gui/sides --html=reports/pytest-side-drinks/index.html
           python3 -m coverage combine
           python3 -m coverage html -d reports/coverage
           python3 -m flake8 --docstring-convention google --format=html --htmldir=reports/flake
           python3 -m pdoc --html --force --output-dir reports/doc .

The major changes:

  • passenv = DISPLAY will tell the tox environment which display to use when loading tkinter elements.
  • We now run coverage in parallel mode, and specify the test folders in the pytest command. This will run three separate sets of tests.
  • Notice that the test reports will now be in different folders. The old reports/pytest folder will no longer be updated.
  • We added a coverage combine command to combine the coverage data from multiple executions of pytest.