primeiro commit com exercicios de Exercism realizados até esta data

This commit is contained in:
2026-10-17 22:10:27 +01:00
commit 58fccbe27a
708 changed files with 35038 additions and 0 deletions
@@ -0,0 +1,24 @@
{
"authors": [
"meatball133",
"BethanyG"
],
"contributors": [
],
"files": {
"solution": [
"dict_methods.py"
],
"test": [
"dict_methods_test.py"
],
"exemplar": [
".meta/exemplar.py"
],
"editor": [
"dict_methods_test_data.py"
]
},
"icon": "gross-store",
"blurb": "Learn about dictionary methods by building a shopping cart MVP for the Mecha Munch grocery app."
}
@@ -0,0 +1 @@
{"track":"python","exercise":"mecha-munch-management","id":"06c21c3131224c7d9bcc64e988c58106","url":"https://exercism.org/tracks/python/exercises/mecha-munch-management","handle":"sanctushck","is_requester":true,"auto_approve":false}
+130
View File
@@ -0,0 +1,130 @@
# Help
## Running the tests
We use [pytest][pytest: Getting Started Guide] as our website test runner.
You will need to install `pytest` on your development machine if you want to run tests for the Python track locally.
You should also install the following `pytest` plugins:
- [pytest-cache][pytest-cache]
- [pytest-subtests][pytest-subtests]
Extended information can be found in our website [Python testing guide][Python track tests page].
### Running Tests
To run the included tests, navigate to the folder where the exercise is stored using `cd` in your terminal (_replace `{exercise-folder-location}` below with your path_).
Test files usually end in `_test.py`, and are the same tests that run on the website when a solution is uploaded.
Linux/MacOS
```bash
$ cd {path/to/exercise-folder-location}
```
Windows
```powershell
PS C:\Users\foobar> cd {path\to\exercise-folder-location}
```
<br>
Next, run the `pytest` command in your terminal, replacing `{exercise_test.py}` with the name of the test file:
Linux/MacOS
```bash
$ python3 -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
```
Windows
```powershell
PS C:\Users\foobar> py -m pytest -o markers=task {exercise_test.py}
==================== 7 passed in 0.08s ====================
```
### Common options
- `-o` : override default `pytest.ini` (_you can use this to avoid marker warnings_)
- `-v` : enable verbose output.
- `-x` : stop running tests on first failure.
- `--ff` : run failures from previous test before running other test cases.
For additional options, use `python3 -m pytest -h` or `py -m pytest -h`.
### Fixing warnings
If you do not use `pytest -o markers=task` when invoking `pytest`, you might receive a `PytestUnknownMarkWarning` for tests that use our new syntax:
```bash
PytestUnknownMarkWarning: Unknown pytest.mark.task - is this a typo? You can register custom marks to avoid this warning - for details, see https://docs.pytest.org/en/stable/mark.html
```
To avoid typing `pytest -o markers=task` for every test you run, you can use a `pytest.ini` configuration file.
We have made one that can be downloaded from the top level of the Python track directory: [pytest.ini][pytest.ini].
You can also create your own `pytest.ini` file with the following content:
```ini
[pytest]
markers =
task: A concept exercise task.
```
Placing the `pytest.ini` file in the _root_ or _working_ directory for your Python track exercises will register the marks and stop the warnings.
More information on pytest marks can be found in the `pytest` documentation on [marking test functions][pytest: marking test functions with attributes] and the `pytest` documentation on [working with custom markers][pytest: working with custom markers].
Information on customizing pytest configurations can be found in the `pytest` documentation on [configuration file formats][pytest: configuration file formats].
### Extending your IDE or Code Editor
Many IDEs and code editors have built-in support for using `pytest` and other code quality tools.
Some community-sourced options can be found on our [Python track tools page][Python track tools page].
[Pytest: Getting Started Guide]: https://docs.pytest.org/en/latest/getting-started.html
[Python track tools page]: https://exercism.org/docs/tracks/python/tools
[Python track tests page]: https://exercism.org/docs/tracks/python/tests
[pytest-cache]:http://pythonhosted.org/pytest-cache/
[pytest-subtests]:https://github.com/pytest-dev/pytest-subtests
[pytest.ini]: https://github.com/exercism/python/blob/main/pytest.ini
[pytest: configuration file formats]: https://docs.pytest.org/en/6.2.x/customize.html#configuration-file-formats
[pytest: marking test functions with attributes]: https://docs.pytest.org/en/6.2.x/mark.html#raising-errors-on-unknown-marks
[pytest: working with custom markers]: https://docs.pytest.org/en/6.2.x/example/markers.html#working-with-custom-markers
## Submitting your solution
You can submit your solution using the `exercism submit dict_methods.py` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [Python track's documentation](https://exercism.org/docs/tracks/python)
- The [Python track's programming category on the forum](https://forum.exercism.org/c/programming/python)
- [Exercism's programming category on the forum](https://forum.exercism.org/c/programming/5)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
Below are some resources for getting help if you run into trouble:
- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources.
- [The Exercism Community on Discord](https://exercism.org/r/discord)
- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community.
- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners.
- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the core developers for the language hang out and get work done.
- [Python Community Forums](https://discuss.python.org/)
- [Free Code Camp Community Forums](https://forum.freecodecamp.org/)
- [CodeNewbie Community Help Tag](https://community.codenewbie.org/t/help)
- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually.
Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already.
If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question.
+57
View File
@@ -0,0 +1,57 @@
# Hints
## General
Remember, this is an [MVP][mvp].
That means you don't need to get too fancy with error handling or different "edge case" scenarios.
It's OK to be simple and direct with the functions you are writing.
The dictionary section of the [official tutorial][dicts-docs] and the mapping type [official library reference][mapping-types-dict] are excellent places to look for more help with all these methods.
## 1. Add Item(s) to the Users Shopping Cart
- You will need to iterate through each item in `items_to_add`.
- You can avoid a `KeyError` when a key is missing by using a `dict` [method][set-default] that takes a _default value_ as one of its arguments.
- It is also possible to accomplish the same thing manually in the `loop` by using some checking and error handling, but the `dict` method is easier.
## 2. Read in Items Listed in the Users Notes App
- Remember, Python's got a method for _everything_. This one is a _classmethod_ that's an easy way to [populate a `dict`][fromkeys] with keys.
- This `dict` method returns a _new dictionary_, populated with default values. If no value is given, the default value will become `None`
## 3. Update Recipe "Ideas" Section
- Don't overthink this one! This can be solved in **one** `dict` method call.
- The key word here is .... [_update_][update].
## 4. Sort the Items in the User Cart
- What method would you call to get an [iterable view of items][items] in the dictionary?
- If you had a `list` or a `tuple`, what [`built-in`][builtins] function might you use to sort them?
- The built-in function you want is the one that returns a _copy_, and doesn't mutate the original.
## 5. Send User Shopping Cart to Store for Fulfillment
- Having a fresh, empty dictionary here as the `fulfillment_cart` might be handy for adding in items.
- `Looping` through the members of the cart might be the most direct way of accessing things here.
- What method would you call to get an [iterable view of just the keys][keys] of the dictionary?
- Remember that you can get the `value` of a given key by using `<dict name>[<key_name>]` syntax.
- If you had a `list` or a `tuple`, what [`built-in`][builtins] function might you use to sort them?
- Remember that the `built-in` function can take an optional `reverse=True` argument.
## 6. Update the Store Inventory to Reflect what a User Has Ordered.
- There is a method that will give you an iterable view of (`key`, `value`) pairs from the dictionary.
- You can access an item in a _nested tuple_ using _bracket notation_: `<tuple>[<nested_tuple>][<index_of_value>]`
- Don't forget to check if an inventory count falls to zero, you'll need to add in the "Out of Stock" message.
[builtins]: https://docs.python.org/3/library/functions.html
[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
[fromkeys]: https://docs.python.org/3/library/stdtypes.html#dict.fromkeys
[items]: https://docs.python.org/3/library/stdtypes.html#dict.items
[keys]: https://docs.python.org/3/library/stdtypes.html#dict.keys
[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
[mvp]: https://en.wikipedia.org/wiki/Minimum_viable_product
[set-default]: https://docs.python.org/3/library/stdtypes.html#dict.setdefault
[update]: https://docs.python.org/3/library/stdtypes.html#dict.update
+364
View File
@@ -0,0 +1,364 @@
# Mecha Munch Management
Welcome to Mecha Munch Management on Exercism's Python Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :)
## Introduction
The `dict` class in Python provides many useful [methods][dict-methods] for working with dictionaries.
Some were introduced in the concept for `dicts`.
Here we cover a few more - along with some techniques for iterating through and manipulating dictionaries.
### Use `setdefault()` for Error-Free Insertion
The dictionary concept previously covered that `.get(key, <default value>)` returns an existing `value` or the `default value` if a `key` is not found in a dictionary, thereby avoiding a `KeyError`.
This works well in situations where you would rather not have extra error handling but cannot trust that a looked-for `key` will be present.
For a similarly "safe" (_without KeyError_) insertion operation, there is the `.setdefault(key, <default value>)` method.
`setdefault(key, <default value>)` will return the `value` if the `key` is found in the dictionary.
If the key is **not** found, it will _insert_ the (`key`, `default value`) pair and return the `default value` for use.
```python
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
# Looking for the value associated with key "Rock Brown".
# The key does not exist, so it is added with the default value, and the value is returned.
>>> palette_I.setdefault('Rock Brown', '#694605')
'#694605'
# The (key, default value) pair has now been added to the dictionary.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Rock Brown': '#694605'}
```
## Use `fromkeys()` to Populate a Dictionary from an Iterable
To quickly populate a dictionary with various `keys` and default values, the _class method_ [`fromkeys(iterable, <default value>)`][fromkeys] will iterate through an iterable of `keys` and create a new `dict`.
All `values` will be set to the `default value` provided:
```python
>>> new_dict = dict.fromkeys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'], 'fill in hex color here')
{'Grassy Green': 'fill in hex color here',
'Purple Mountains Majesty': 'fill in hex color here',
'Misty Mountain Pink': 'fill in hex color here'}
```
## Iterating Over Entries in a Dictionary Via Views
The `.keys()`, `.values()`, and `.items()` methods return [_iterable views_][dict-views] of a dictionary.
These views can be used to easily loop over entries without altering them.
Views are also _dynamic_ -- when underlying dictionary data changes, the associated `view object` will reflect the change:
```python
>>> palette_I = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd'}
# Using .keys() returns a list of keys.
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink'])
# Using .values() returns a list of values.
>>> palette_I.values()
dict_values(['#9bc400', '#8076a3', '#f9c5bd'])
# Using .items() returns a list of (key, value) tuples.
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', '#8076a3'), ('Misty Mountain Pink', '#f9c5bd')])
# Views are dynamic. Changing values in the dict changes all of the associated views.
>>> palette_I['Purple Mountains Majesty'] = (128, 118, 163)
>>> palette_I['Deep Red'] = '#932432'
>>> palette_I.values()
dict_values(['#9bc400', (128, 118, 163), '#f9c5bd', '#932432'])
>>> palette_I.keys()
dict_keys(['Grassy Green', 'Purple Mountains Majesty', 'Misty Mountain Pink', 'Deep Red'])
>>> palette_I.items()
dict_items([('Grassy Green', '#9bc400'), ('Purple Mountains Majesty', (128, 118, 163)), ('Misty Mountain Pink', '#f9c5bd'), ('Deep Red', '#932432')])
```
## More on `.keys()`, `.values()`, and `.items()`
In Python 3.7+, `dicts` preserve the order in which entries are inserted allowing First-in, First-out (_`FIFO`_), iteration when using `.keys()`, `.values()`, or `.items()`.
In Python 3.8+, views are also _reversible_.
This allows keys, values, or (`key`, `value`) pairs to be iterated over in Last-in, First-out (`LIFO`) order by using `reversed(<dict>.keys())`, `reversed(<dict>.values())`, or `reversed(<dict>.items())`:
```python
>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
# Iterating in insertion order
>>> for item in palette_II.items():
... print(item)
...
('Factory Stone Purple', '#7c677f')
('Green Treeline', '#478559')
('Purple baseline', '#161748')
# Iterating in the reverse direction.
>>> for item in reversed(palette_II.items()):
... print (item)
...
('Purple baseline', '#161748')
('Green Treeline', '#478559')
('Factory Stone Purple', '#7c677f')
```
## Sorting a Dictionary
Dictionaries do not have a built-in sorting method.
However, it is possible to sort a `dict` _view_ using the built-in function `sorted()` with `.items()`.
The sorted view can then be used to create a new dictionary.
Like iteration, the default sort is over dictionary `keys`.
```python
# Default ordering for a dictionary is last in, first out (LIFO).
>>> color_palette = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
# The default sort order for a dictionary uses the keys.
>>> sorted_palette = dict(sorted(color_palette.items()))
>>> sorted_palette
{'Factory Stone Purple': '#7c677f',
'Grassy Green': '#9bc400',
'Green Treeline': '#478559',
'Misty Mountain Pink': '#f9c5bd',
'Purple Mountains Majesty': '#8076a3',
'Purple baseline': '#161748'}
```
## Combining Dictionaries with `.update()`
`<dict_one>.update(<dict_two>)` can be used to _combine_ two dictionaries.
This method will take the (`key`,`value`) pairs of `<dict_two>` and write them into `<dict_one>`:
```python
>>> palette_I = {'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple Baseline': '#161748'}
>>> palette_I.update(palette_II)
# Note that new items from palette_II are added.
>>> palette_I
{'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd', 'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple Baseline': '#161748'}
```
Where keys in the two dictionaries _overlap_, the `value` in `dict_one` will be _overwritten_ by the corresponding `value` from `dict_two`:
```python
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> palette_III = {'Grassy Green': (155, 196, 0), 'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189)}
>>> palette_I.update(palette_III)
# Overlapping values in palette_I are replaced with values from palette_III
>>> palette_I
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189),
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559', 'Purple baseline': '#161748'}
```
## Merge or Update Dictionaries Via the Union (`|`) Operators
Python 3.9 introduces a different means of merging `dicts`: the `union` operators.
`dict_one | dict_two` will create a **new dictionary**, made up of the (`key`, `value`) pairs of `dict_one` and `dict_two`.
When both dictionaries share keys, `dict_two` values take precedence.
```python
>>> palette_I = {'Grassy Green': '#9bc400', 'Purple Mountains Majesty': '#8076a3', 'Misty Mountain Pink': '#f9c5bd'}
>>> palette_II = {'Factory Stone Purple': '#7c677f', 'Green Treeline': '#478559', 'Purple baseline': '#161748'}
>>> new_dict = palette_I | palette_II
>>> new_dict
...
{'Grassy Green': '#9bc400',
'Purple Mountains Majesty': '#8076a3',
'Misty Mountain Pink': '#f9c5bd',
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
```
`dict_one |= other` behaves similar to `<dict_one>.update(<other>)`, but in this case, `other` can be either a `dict` or an iterable of (`key`, `value`) pairs:
```python
>>> palette_III = {'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189)}
>>> new_dict |= palette_III
>>> new_dict
...
{'Grassy Green': (155, 196, 0),
'Purple Mountains Majesty': (128, 118, 163),
'Misty Mountain Pink': (249, 197, 189),
'Factory Stone Purple': '#7c677f',
'Green Treeline': '#478559',
'Purple baseline': '#161748'}
```
For a detailed explanation of dictionaries and methods for working with them, the [official tutorial][dicts-docs] and the [official library reference][mapping-types-dict] are excellent starting places.
[Real Python][how-to-dicts] and [Finxter][fi-dict-guide] also have very thorough articles on Python dictionaries.
[dict-methods]: https://docs.python.org/3/library/stdtypes.html#dict
[dict-views]: https://docs.python.org/3/library/stdtypes.html#dict-views
[dicts-docs]: https://docs.python.org/3/tutorial/datastructures.html#dictionaries
[fi-dict-guide]: https://blog.finxter.com/python-dictionary
[fromkeys]: https://docs.python.org/3/library/stdtypes.html#dict.fromkeys
[how-to-dicts]: https://realpython.com/python-dicts/
[mapping-types-dict]: https://docs.python.org/3/library/stdtypes.html#mapping-types-dict
## Instructions
Mecha Munch™, a grocery shopping automation company has just hired you to work on their ordering app.
Your team is tasked with building an MVP (_[minimum viable product][mvp]_) that manages all the basic shopping cart activities, allowing users to add, remove, and sort their grocery orders.
Thankfully, a different team is handling all the money and check-out functions!
## 1. Add Item(s) to the Users Shopping Cart
The MVP should allow the user to add items to their shopping cart.
This could be a single item or multiple items at once.
Since this is an MVP, item quantity is indicated by _repeats_.
If a user wants to add 2 Oranges, 'Oranges' will appear twice in the input iterable.
If the user already has the item in their cart, the cart quantity should be increased by 1.
If the item is _new_ to the cart, it should be added with a quantity of 1.
Create the function `add_item(<current_cart>, <items_to_add>)` that takes a cart dictionary and any list-like iterable of items to add as arguments.
It should return a new/updated shopping cart dictionary for the user.
```python
>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
('Apple', 'Apple', 'Orange', 'Apple', 'Banana'))
{'Banana': 4, 'Apple': 5, 'Orange': 2}
>>> add_item({'Banana': 3, 'Apple': 2, 'Orange': 1},
['Banana', 'Orange', 'Blueberries', 'Banana'])
{'Banana': 5, 'Apple': 2, 'Orange': 2, 'Blueberries': 1}
```
## 2. Read in Items Listed in the Users Notes App
Uh-oh.
Looks like the product team is engaging in [feature creep][feature creep].
They want to add extra functionality to the MVP.
The application now has to create a shopping cart by reading items off a users notes app.
Convenient for the users, but slightly more work for the team.
Create the function `read_notes(<notes>)` that can take any list-like iterable as an argument.
The function should parse the items and create a user shopping cart/dictionary.
Each item should be added with a quantity of 1.
The new user cart should then be returned.
```python
>>> read_notes(('Banana','Apple', 'Orange'))
{'Banana': 1, 'Apple': 1, 'Orange': 1}
>>> read_notes(['Blueberries', 'Pear', 'Orange', 'Banana', 'Apple'])
{'Blueberries' : 1, 'Pear' : 1, 'Orange' : 1, 'Banana' : 1, 'Apple' : 1}
```
## 3. Update Recipe "Ideas" Section
The app has an "ideas" section that's filled with finished recipes from various cuisines.
The user can select any one of these recipes and have all its ingredients added to their shopping cart automatically.
The project manager has asked you create a way to edit these "ideas" recipes, since the content team keeps changing around ingredients and quantities.
Create the function `update_recipes(<ideas>, <recipe_updates>)` that takes an "ideas" dictionary and an iterable of recipe updates as arguments.
The function should return the new/updated "ideas" dictionary.
```python
>>> update_recipes({'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}},
(('Banana Bread', {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3}),))
...
{'Banana Bread' : {'Banana': 4, 'Walnuts': 2, 'Flour': 1, 'Butter': 1, 'Milk': 2, 'Eggs': 3},
'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1}}
>>> update_recipes({'Banana Bread' : {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie' : {'Raspberry': 1, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1},
'Pasta Primavera': {'Eggs': 1, 'Carrots': 1, 'Spinach': 2, 'Tomatoes': 3, 'Parmesan': 2, 'Milk': 1, 'Onion': 1}},
[('Raspberry Pie', {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2}),
('Pasta Primavera', {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1}),
('Blueberry Crumble', {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3})])
...
{'Banana Bread': {'Banana': 1, 'Apple': 1, 'Walnuts': 1, 'Flour': 1, 'Eggs': 2, 'Butter': 1},
'Raspberry Pie': {'Raspberry': 3, 'Orange': 1, 'Pie Crust': 1, 'Cream Custard': 1, 'Whipped Cream': 2},
'Pasta Primavera': {'Eggs': 1, 'Mixed Veggies': 2, 'Parmesan': 2, 'Milk': 1, 'Spinach': 1, 'Bread Crumbs': 1},
'Blueberry Crumble': {'Blueberries': 2, 'Whipped Creme': 2, 'Granola Topping': 2, 'Yogurt': 3}}
```
## 4. Sort the Items in the User Cart
Once a user has started a cart, the app allows them to sort their items alphabetically.
This makes things easier to find, and helps when there are data-entry errors like having 'potatoes' and 'Potato' in the database.
Create the function `sort_entries(<cart>)` that takes a shopping cart/dictionary as an argument and returns a new, alphabetically sorted one.
```python
>>> sort_entries({'Banana': 3, 'Apple': 2, 'Orange': 1})
{'Apple': 2, 'Banana':3, 'Orange': 1}
```
## 5. Send User Shopping Cart to Store for Fulfillment
The app needs to send a given users cart to the store for fulfillment.
However, the shoppers in the store need to know which store aisle the item can be found in and if the item needs refrigeration.
So (_rather arbitrarily_) the "fulfillment cart" needs to be sorted in reverse alphabetical order with item quantities combined with location and refrigeration information.
Create the function `send_to_store(<cart>, <aisle_mapping>)` that takes a user shopping cart and a dictionary that has store aisle number and a `True`/`False` for refrigeration needed for each item.
The function should `return` a combined "fulfillment cart" that has (quantity, aisle, and refrigeration) for each item the customer is ordering.
Items should appear in _reverse_ alphabetical order.
```python
>>> send_to_store({'Banana': 3, 'Apple': 2, 'Orange': 1, 'Milk': 2},
{'Banana': ['Aisle 5', False], 'Apple': ['Aisle 4', False], 'Orange': ['Aisle 4', False], 'Milk': ['Aisle 2', True]})
{'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]}
```
## 6. Update the Store Inventory to Reflect what a User Has Ordered.
The app can't just place customer orders endlessly.
Eventually, the store is going to run out of various products.
So your app MVP needs to update the store inventory every time a user sends their order to the store.
Otherwise, customers will order products that aren't actually available.
Create the function `update_store_inventory(<fulfillment_cart>, <store_inventory>)` that takes a "fulfillment cart" and a store inventory.
The function should reduce the store inventory amounts by the number "ordered" in the "fulfillment cart" and then return the updated store inventory.
Where a store item count falls to 0, the count should be replaced by the message 'Out of Stock'.
```python
>>> update_store_inventory({'Orange': [1, 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True], 'Banana': [3, 'Aisle 5', False], 'Apple': [2, 'Aisle 4', False]},
{'Banana': [15, 'Aisle 5', False], 'Apple': [12, 'Aisle 4', False], 'Orange': [1, 'Aisle 4', False], 'Milk': [4, 'Aisle 2', True]})
{'Banana': [12, 'Aisle 5', False], 'Apple': [10, 'Aisle 4', False], 'Orange': ['Out of Stock', 'Aisle 4', False], 'Milk': [2, 'Aisle 2', True]}
```
[feature creep]: https://en.wikipedia.org/wiki/Feature_creep
[mvp]: https://en.wikipedia.org/wiki/Minimum_viable_product
## Source
### Created by
- @meatball133
- @BethanyG
@@ -0,0 +1,93 @@
"""Functions to manage a users shopping cart items."""
from re import fullmatch
def add_item(current_cart, items_to_add):
"""Add items to shopping cart.
:param current_cart: dict - the current shopping cart.
:param items_to_add: iterable - items to add to the cart.
:return: dict - the updated user cart dictionary.
"""
# create a new cart with items_to_add if current_cart is empty
# if current_cart == {}:
# current_cart = dict.fromkeys(items_to_add, 0)
# for item in items_to_add:
# current_cart[item] += 1
# return current_cart
# (after doing this i realized that the bellow code also addresses the
# concern of the current_cart being empty, so i commented out the code
# above)
# increment item by one if item already exists in current_cart,
# otherwise add new item to current_cart with value of 1
for item in items_to_add:
current_cart[item] = current_cart.get(item, 0) + 1
return current_cart
def read_notes(notes):
"""Create user cart from an iterable notes entry.
:param notes: iterable of items to add to cart.
:return: dict - a user shopping cart dictionary.
"""
shopping_cart: dict = dict.fromkeys(notes, 0)
for item in notes:
shopping_cart[item] += 1
return shopping_cart
def update_recipes(ideas, recipe_updates):
"""Update the recipe ideas dictionary.
:param ideas: dict - The "recipe ideas" dict.
:param recipe_updates: dict - dictionary with updates for the ideas section.
:return: dict - updated "recipe ideas" dict.
"""
ideas |= recipe_updates
return ideas
def sort_entries(cart):
"""Sort a users shopping cart in alphabetically order.
:param cart: dict - a users shopping cart dictionary.
:return: dict - users shopping cart sorted in alphabetical order.
"""
sorted_cart: dict = dict(sorted(cart.items()))
return sorted_cart
def send_to_store(cart, aisle_mapping):
"""Combine users order to aisle and refrigeration information.
:param cart: dict - users shopping cart dictionary.
:param aisle_mapping: dict - aisle and refrigeration information dictionary.
:return: dict - fulfillment dictionary ready to send to store.
"""
fulfillment: dict[str, list] = {}
for item in cart:
fulfillment[item] = [cart[item],
aisle_mapping[item][0], aisle_mapping[item][1]]
fulfillment = dict(sorted(fulfillment.items(), reverse=True))
return fulfillment
def update_store_inventory(fulfillment_cart, store_inventory):
"""Update store inventory levels with user order.
:param fulfillment cart: dict - fulfillment cart to send to store.
:param store_inventory: dict - store available inventory
:return: dict - store_inventory updated.
"""
for item in fulfillment_cart:
new_stock = store_inventory[item][0] - fulfillment_cart[item][0]
store_inventory[item] = [
"Out of Stock" if new_stock <= 0 else new_stock,
store_inventory[item][1],
store_inventory[item][2],
]
return store_inventory
@@ -0,0 +1,94 @@
import unittest
import pytest
from collections import OrderedDict
from dict_methods import (
add_item,
read_notes,
update_recipes,
sort_entries,
send_to_store,
update_store_inventory,
)
from dict_methods_test_data import (
add_item_data,
read_notes_data,
update_recipes_data,
sort_entries_data,
send_to_store_data,
update_store_inventory_data,
)
class MechaMunchManagementTest(unittest.TestCase):
@pytest.mark.task(taskno=1)
def test_add_item(self):
for variant, (input_data, expected) in enumerate(add_item_data, start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, expected=expected):
actual_result = add_item(input_data[0], input_data[1])
error_msg= (f'Called add_item({input_data[0]}, {input_data[1]}). '
f'The function returned {actual_result}, but the tests '
f'expected: {expected} once the item was added.')
self.assertEqual(actual_result, expected, msg=error_msg)
@pytest.mark.task(taskno=2)
def test_read_notes(self):
for variant, (input_data, expected) in enumerate(read_notes_data, start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, expected=expected):
actual_result = read_notes(input_data)
error_msg = (f'Called read_notes({input_data}). '
f'The function returned {actual_result}, but the tests '
f'expected: {expected} once the notes were read.')
self.assertEqual(actual_result, expected, msg=error_msg)
@pytest.mark.task(taskno=3)
def test_update_recipes(self):
for variant, (input_data, expected) in enumerate(update_recipes_data, start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, expected=expected):
actual_result = update_recipes(input_data[0], input_data[1])
error_msg = (f'Called update_recipes({input_data[0]}, {input_data[1]}). '
f'The function returned {actual_result}, but the tests '
f'expected: {expected} once the recipes were updated.')
self.assertEqual(actual_result, expected, msg=error_msg)
@pytest.mark.task(taskno=4)
def test_sort_entries(self):
for variant, (input_data, expected) in enumerate(sort_entries_data, start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, expecred=expected):
actual_result = sort_entries(input_data)
error_msg = (f'Called sort_entries({input_data}). '
f'The function returned {actual_result}, but the tests '
f'expected: {expected} for the sorted entries.')
# Because we are asserting equal, we need to convert to an OrderedDict.
# Regular dictionaries will compare equal even when they are ordered
# differently from one another. See https://stackoverflow.com/a/58961124
self.assertEqual(OrderedDict(actual_result), OrderedDict(expected), msg=error_msg)
@pytest.mark.task(taskno=5)
def test_send_to_store(self):
for variant, (input_data, expected) in enumerate(send_to_store_data, start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, expected=expected):
actual_result = send_to_store(input_data[0], input_data[1])
error_msg = (f'Called send_to_store({input_data[0]}, {input_data[1]}). '
f'The function returned {actual_result}, but the tests '
f'expected: {expected} as the fulfillment cart.')
# Because we are asserting equal, we need to convert to an OrderedDict.
# Regular dictionaries will compare equal even when they are ordered
# differently from one another. See https://stackoverflow.com/a/58961124
self.assertEqual(OrderedDict(actual_result), OrderedDict(expected), msg=error_msg)
@pytest.mark.task(taskno=6)
def test_update_store_inventory(self):
for variant, (input_data, expected) in enumerate(update_store_inventory_data, start=1):
with self.subTest(f'variation #{variant}', input_data=input_data, expected=expected):
actual_result = update_store_inventory(input_data[0], input_data[1])
error_msg = (f'Called update_store_inventory({input_data[0]}, {input_data[1]}). '
f'The function returned {actual_result}, but the tests '
f'expected: {expected} as the store inventory.')
self.assertEqual(actual_result, expected, msg=error_msg)
@@ -0,0 +1,394 @@
## add_item test cases##
add_item_inputs = [
({"Apple": 1, "Banana": 4}, ("Apple", "Banana", "Orange")),
(
{"Orange": 1, "Raspberry": 1, "Blueberries": 10},
["Raspberry", "Blueberries", "Raspberry"],
),
(
{"Broccoli": 1, "Banana": 1},
("Broccoli", "Kiwi", "Kiwi", "Kiwi", "Melon", "Apple", "Banana", "Banana"),
),
]
add_item_outputs = [
{"Apple": 2, "Banana": 5, "Orange": 1},
{"Orange": 1, "Raspberry": 3, "Blueberries": 11},
{"Broccoli": 2, "Banana": 3, "Kiwi": 3, "Melon": 1, "Apple": 1},
]
add_item_data = zip(add_item_inputs, add_item_outputs)
## read_notes test cases##
read_notes_inputs = [
("Apple", "Banana"),
("Orange", "Raspberry", "Blueberries"),
["Broccoli", "Kiwi", "Melon", "Apple", "Banana"],
]
read_notes_outputs = [
{"Apple": 1, "Banana": 1},
{"Orange": 1, "Raspberry": 1, "Blueberries": 1},
{"Broccoli": 1, "Kiwi": 1, "Melon": 1, "Apple": 1, "Banana": 1},
]
read_notes_data = zip(read_notes_inputs, read_notes_outputs)
## update_recipes test cases##
update_recipes_inputs = [
(
{
"Banana Bread": {
"Banana": 1,
"Apple": 1,
"Walnuts": 1,
"Flour": 1,
"Eggs": 2,
"Butter": 1,
},
"Raspberry Pie": {
"Raspberry": 1,
"Orange": 1,
"Pie Crust": 1,
"Cream Custard": 1,
},
},
(
(
"Banana Bread",
{
"Banana": 4,
"Walnuts": 2,
"Flour": 1,
"Butter": 1,
"Milk": 2,
"Eggs": 3,
},
),
),
),
(
{
"Apple Pie": {"Apple": 1, "Pie Crust": 1, "Cream Custard": 1},
"Blueberry Pie": {"Blueberries": 1, "Pie Crust": 1, "Cream Custard": 1},
},
(
("Blueberry Pie", {"Blueberries": 2,
"Pie Crust": 1, "Cream Custard": 1}),
("Apple Pie", {"Apple": 1, "Pie Crust": 1, "Cream Custard": 1}),
),
),
(
{
"Banana Bread": {
"Banana": 1,
"Apple": 1,
"Walnuts": 1,
"Flour": 1,
"Eggs": 2,
"Butter": 1,
},
"Raspberry Pie": {
"Raspberry": 1,
"Orange": 1,
"Pie Crust": 1,
"Cream Custard": 1,
},
"Pasta Primavera": {
"Eggs": 1,
"Carrots": 1,
"Spinach": 2,
"Tomatoes": 3,
"Parmesan": 2,
"Milk": 1,
"Onion": 1,
},
},
(
(
"Raspberry Pie",
{
"Raspberry": 3,
"Orange": 1,
"Pie Crust": 1,
"Cream Custard": 1,
"Whipped Cream": 2,
},
),
(
"Pasta Primavera",
{
"Eggs": 1,
"Mixed Veggies": 2,
"Parmesan": 2,
"Milk": 1,
"Spinach": 1,
"Bread Crumbs": 1,
},
),
(
"Blueberry Crumble",
{
"Blueberries": 2,
"Whipped Creme": 2,
"Granola Topping": 2,
"Yogurt": 3,
},
),
),
),
]
update_recipes_outputs = [
{
"Banana Bread": {
"Banana": 4,
"Walnuts": 2,
"Flour": 1,
"Butter": 1,
"Milk": 2,
"Eggs": 3,
},
"Raspberry Pie": {
"Raspberry": 1,
"Orange": 1,
"Pie Crust": 1,
"Cream Custard": 1,
},
},
{
"Apple Pie": {"Apple": 1, "Pie Crust": 1, "Cream Custard": 1},
"Blueberry Pie": {"Blueberries": 2, "Pie Crust": 1, "Cream Custard": 1},
},
{
"Banana Bread": {
"Banana": 1,
"Apple": 1,
"Walnuts": 1,
"Flour": 1,
"Eggs": 2,
"Butter": 1,
},
"Raspberry Pie": {
"Raspberry": 3,
"Orange": 1,
"Pie Crust": 1,
"Cream Custard": 1,
"Whipped Cream": 2,
},
"Pasta Primavera": {
"Eggs": 1,
"Mixed Veggies": 2,
"Parmesan": 2,
"Milk": 1,
"Spinach": 1,
"Bread Crumbs": 1,
},
"Blueberry Crumble": {
"Blueberries": 2,
"Whipped Creme": 2,
"Granola Topping": 2,
"Yogurt": 3,
},
},
]
update_recipes_data = zip(update_recipes_inputs, update_recipes_outputs)
## sort_entries test cases##
sort_entries_inputs = [
{"Banana": 4, "Apple": 2, "Orange": 1, "Pear": 12},
{"Apple": 3, "Orange": 5, "Banana": 1, "Avocado": 2},
{"Orange": 3, "Banana": 2, "Apple": 1},
{
"Apple": 2,
"Raspberry": 2,
"Blueberries": 5,
"Broccoli": 2,
"Kiwi": 1,
"Melon": 4,
},
]
sort_entries_outputs = [
{"Apple": 2, "Banana": 4, "Orange": 1, "Pear": 12},
{"Apple": 3, "Avocado": 2, "Banana": 1, "Orange": 5},
{"Apple": 1, "Banana": 2, "Orange": 3},
{
"Apple": 2,
"Blueberries": 5,
"Broccoli": 2,
"Kiwi": 1,
"Melon": 4,
"Raspberry": 2,
},
]
sort_entries_data = zip(sort_entries_inputs, sort_entries_outputs)
## send_to_store test cases##
send_to_store_inputs = [
(
{"Banana": 3, "Apple": 2, "Orange": 1, "Milk": 2},
{
"Banana": ["Aisle 5", False],
"Apple": ["Aisle 4", False],
"Orange": ["Aisle 4", False],
"Milk": ["Aisle 2", True],
},
),
(
{"Kiwi": 3, "Juice": 5, "Yoghurt": 2, "Milk": 5},
{
"Kiwi": ["Aisle 6", False],
"Juice": ["Aisle 5", False],
"Yoghurt": ["Aisle 2", True],
"Milk": ["Aisle 2", True],
},
),
(
{
"Apple": 2,
"Raspberry": 2,
"Blueberries": 5,
"Broccoli": 2,
"Kiwi": 1,
"Melon": 4,
},
{
"Apple": ["Aisle 1", False],
"Raspberry": ["Aisle 6", False],
"Blueberries": ["Aisle 6", False],
"Broccoli": ["Aisle 3", False],
"Kiwi": ["Aisle 6", False],
"Melon": ["Aisle 6", False],
},
),
(
{"Orange": 1},
{
"Banana": ["Aisle 5", False],
"Apple": ["Aisle 4", False],
"Orange": ["Aisle 4", False],
"Milk": ["Aisle 2", True],
},
),
(
{"Banana": 3, "Apple": 2, "Orange": 1},
{
"Banana": ["Aisle 5", False],
"Apple": ["Aisle 4", False],
"Orange": ["Aisle 4", False],
"Milk": ["Aisle 2", True],
},
),
]
send_to_store_outputs = [
{
"Orange": [1, "Aisle 4", False],
"Milk": [2, "Aisle 2", True],
"Banana": [3, "Aisle 5", False],
"Apple": [2, "Aisle 4", False],
},
{
"Yoghurt": [2, "Aisle 2", True],
"Milk": [5, "Aisle 2", True],
"Kiwi": [3, "Aisle 6", False],
"Juice": [5, "Aisle 5", False],
},
{
"Raspberry": [2, "Aisle 6", False],
"Melon": [4, "Aisle 6", False],
"Kiwi": [1, "Aisle 6", False],
"Broccoli": [2, "Aisle 3", False],
"Blueberries": [5, "Aisle 6", False],
"Apple": [2, "Aisle 1", False],
},
{"Orange": [1, "Aisle 4", False]},
{
"Orange": [1, "Aisle 4", False],
"Banana": [3, "Aisle 5", False],
"Apple": [2, "Aisle 4", False],
},
]
send_to_store_data = zip(send_to_store_inputs, send_to_store_outputs)
## update_store_inventory test cases##
update_store_inventory_inputs = [
(
{
"Orange": [1, "Aisle 4", False],
"Milk": [2, "Aisle 2", True],
"Banana": [3, "Aisle 5", False],
"Apple": [2, "Aisle 4", False],
},
{
"Banana": [15, "Aisle 5", False],
"Apple": [12, "Aisle 4", False],
"Orange": [1, "Aisle 4", False],
"Milk": [4, "Aisle 2", True],
},
),
(
{"Kiwi": [3, "Aisle 6", False]},
{
"Kiwi": [3, "Aisle 6", False],
"Juice": [5, "Aisle 5", False],
"Yoghurt": [2, "Aisle 2", True],
"Milk": [5, "Aisle 2", True],
},
),
(
{
"Kiwi": [1, "Aisle 6", False],
"Melon": [4, "Aisle 6", False],
"Apple": [2, "Aisle 1", False],
"Raspberry": [2, "Aisle 6", False],
"Blueberries": [5, "Aisle 6", False],
"Broccoli": [1, "Aisle 3", False],
},
{
"Apple": [2, "Aisle 1", False],
"Raspberry": [5, "Aisle 6", False],
"Blueberries": [10, "Aisle 6", False],
"Broccoli": [4, "Aisle 3", False],
"Kiwi": [1, "Aisle 6", False],
"Melon": [8, "Aisle 6", False],
},
),
]
update_store_inventory_outputs = [
{
"Banana": [12, "Aisle 5", False],
"Apple": [10, "Aisle 4", False],
"Orange": ["Out of Stock", "Aisle 4", False],
"Milk": [2, "Aisle 2", True],
},
{
"Juice": [5, "Aisle 5", False],
"Yoghurt": [2, "Aisle 2", True],
"Milk": [5, "Aisle 2", True],
"Kiwi": ["Out of Stock", "Aisle 6", False],
},
{
"Kiwi": ["Out of Stock", "Aisle 6", False],
"Melon": [4, "Aisle 6", False],
"Apple": ["Out of Stock", "Aisle 1", False],
"Raspberry": [3, "Aisle 6", False],
"Blueberries": [5, "Aisle 6", False],
"Broccoli": [3, "Aisle 3", False],
},
]
update_store_inventory_data = zip(
update_store_inventory_inputs, update_store_inventory_outputs
)