Event-Driven Programming
This page lists the milestone requirements for Milestone 7 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 Game Grub, offering food of all kinds to celebrate our love of games of all kinds.
The seventh milestone involves dealing with the various events generated by the GUI and constructing a list of items that represent an order.
General Requirements
Assignment Requirements
Changes to Previous Milestone
-
Changes to the previous milestone:
- Add a Cancel button to each of the entree, side, or drink panels.
- Add a Delete button to the
OrderPanel
panel. - Switch the list box in the
OrderPanel
to a tree element (JavaJTree
or tkinterTreeview
). See the associated example project for code you can use for this.
See the updated GUI mockups below for some design ideas.
Order Tree Functionality
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
OrderPanel
, the following should happen:- The item’s string representation should be a top-level node in the tree.
- Any 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.
It may be helpful to maintain a hash map or dictionary in the OrderPanel
class that associates nodes in the GUI tree element with the actual Item
instances that they represent. This is in addition to the Order
object they will be stored in (added in a later milestone).
Save Buttons
When the Save button in any of the entree, 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
OrderPanel
if it is a new item, or the item should be updated if it is being edited. - The main panel in
PrimaryWindow
should be replaced with theMenuPanel
(this was part of the previous milestone).
Cancel Button
When the Cancel button in any of the entree, 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
PrimaryWindow
should be replaced with theOrderPanel
(this was part of the previous milestone for the Save button, and the code is similar).
Edit Button
When the Edit button in the OrderPanel
is clicked, the following should happen:
- The
Item
that is currently selected should be determined. If the selection is an child of that item, the code should work upwards in the tree to find the relatedItem
. - The appropriate entree, side, or drink panel should be loaded into the main panel in
PrimaryWindow
and populated with the current data from the item (most of this should be present from the previous milestone, but much of it may be untested at this point). - 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.
Delete Button
When the Delete button in the SidebarPanel
is clicked, the following should happen:
- The
Item
that is currently selected should be determined. If the selection is an child of that item, the code should work upwards in the tree to find the relatedItem
. - That item should be removed from the tree element and any other relevant data structures in the
OrderPanel
class.
New Unit Tests
Unit tests should be added to the corresponding test package for the following classes:
- Each entree panel in
gamegrub.gui.entrees
- Each drink panel in
gamegrub.gui.drinks
- The side panel in
gamegrub.gui.sides
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 PrimaryWindow
, OrderPanel
, or MenuPanel
classes in this milestone. - Russ
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.
Documentation and Tests
Finally, the following requirements from the previous milestone are continued:
- Classes and unit tests in the
gamegrub.gui
package and sub-packages do require all appropriate documentation comments, and must be free of style errors. Every method must include full documentation comments. - Classes in the
gamegrub.gui
base package do not require unit tests, but all entree, drink, and side panels require unit tests as outlined above. - Classes and unit tests in the
gamegrub.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. - Update the GUI UML Diagram Contained in this project to match the updated structure of the GUI portions of this 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.
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 one of the more complex panels, and I used that as a template to create the rest of the unit tests. My current model solution contains ~850 unit tests, and I was able to achieve 100% code coverage on all GUI item 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.
- Any portion of the project which does not meet the general requirements listed above will have a commensurate amount of points deducted.
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.
As part of the grading of all assignments in this course, I will be doing a deep dive into a few classes in your code. This will include leaving detailed comments on code style and format in GitHub. I will usually choose various classes to review at random, and any issues found in that class will be verified in other classes of the same type. For any GUI portions, I’ll also be testing the functionality of the GUI for each class under review. - Russ
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
Item Panel
Helpful Methods
I found these methods helpful in my solution:
PrimaryWindow.addItem(item)
- basically a pass-through method that calls theOrderPanel.addItem(item)
method. This method would be accessible to all order item panels since they get a reference to thePrimaryWindow
instance.OrderPanel.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 toppings 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 theis
operator in Python. This means that you won’t be able to use the normalcontains()
orin
method for determining if the item is already in a list - you must iterate through the list manually. OrderPanel.updateTree(item, node)
- handles actually updating the tree. Ifnode
isnull
or not provided, it creates a new one, otherwise it uses the existingnode
and updates it. It should return thenode
or that node’sid
when complete.
Unit Tests
Entree Panels
Each entree panel test class should contain unit tests for the following:
- If a panel is created without providing an existing item, it should create a new instance of that item in its internal attribute.
- Call the
actionPerformed()
method with an invalid action command, and assert that an exception is not thrown (it should not do anything). - Instantiate the panel by providing an existing item and setting GUI elements accordingly:
- For each base, verify that if the existing item is using that base, the base combo box is set correctly.
- For each ingredient, verify that if the existing item does or does not include that ingredient, the corresponding checkbox is set correctly.
- For each topping, verify that if the existing item does or does not include that topping, the corresponding checkbox is set correctly.
- Fire the “save” event after changing GUI elements:
- For each base, verify that if that base is selected in the base combo box, the saved item now has the correct base value.
- For each ingredient, verify that if that ingredient is or is not selected, the saved item now has the correct value for that ingredient.
- For each topping, verify that if that topping is or is not selected, the saved item now has the correct value for that topping.
- Fire the “cancel” event after changing GUI elements:
- 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:
- For each side, create the panel by providing an instance of that side (or the name of the side) and test that the item is set to an instance of that class.
- Confirm that the penal will throw an exception if it is instantiated without providing an instance of a side (or the name of a side).
- Call the
actionPerformed()
method with an invalid action command, and assert that an exception is not thrown (it should not do anything). - Instantiate the panel by providing an existing item and setting GUI elements accordingly:
- For each size, verify that if the existing item is that size, the size combo box is set correctly.
- Fire the “cancel” event after changing GUI elements:
- 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.
Drink Panels
Each drink panel test class should contain unit tests for the following:
- If a panel is created without providing an existing item, it should create a new instance of that item in its internal attribute.
- Call the
actionPerformed()
method with an invalid action command, and assert that an exception is not thrown (it should not do anything). - Instantiate the panel by providing an existing item and setting GUI elements accordingly:
- For each size, verify that if the existing item is that size, the size combo box is set correctly.
- For each ingredient, verify that if the existing item does or does not include that ingredient, the corresponding checkbox is set correctly.
- Fire the “save” event after changing GUI elements:
- For each size, verify that if the existing item is that size, the size combo box is set correctly.
- For each ingredient, verify that if that ingredient is or is not selected, the saved item now has the correct value for that ingredient.
- Fire the “cancel” event after changing GUI elements:
- 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.
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 = py39
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/gamegrub/data --html=reports/pytest-data/index.html
python3 -m coverage run --parallel-mode --source src -m pytest test/gamegrub/gui/entrees --html=reports/pytest-entrees/index.html
python3 -m coverage run --parallel-mode --source src -m pytest test/gamegrub/gui/drinks test/gamegrub/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.