diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..17e15f27e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Current File", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..3f25bfb70 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "python.pythonPath": "venv/bin/python", + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.nosetestsEnabled": false, + "python.testing.pytestEnabled": true +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 02a7ff75c..3ebd6d8aa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,35 +1,35 @@ # Install latest version of node -FROM continuumio/anaconda3:latest +FROM continuumio/anaconda3: latest -RUN apt-get update && apt-get install -y \ +RUN apt-get update & & apt-get install - y \ build-essential \ ca-certificates \ curl \ sudo \ openssl \ libssl-dev libffi-dev \ - --no-install-recommends + - -no-install-recommends # Create directory for app -RUN mkdir /app +RUN mkdir / app # Set as current directory for RUN, ADD, COPY commands -WORKDIR /app +WORKDIR / app # Add to PATH -ENV PATH /app:$PATH +ENV PATH / app: $PATH # Add requirements.txt from upstream -ADD requirements.txt /app -RUN pip install -r /app/requirements.txt +ADD requirements.txt / app +RUN pip install - r / app/requirements.txt # Add entire student fork (overwrites previously added package.json) ARG SUBMISSION_SUBFOLDER -ADD $SUBMISSION_SUBFOLDER /app +ADD $SUBMISSION_SUBFOLDER / app # Overwrite files in student fork with upstream files -ADD test.sh /app -ADD tests /app/tests +ADD test.sh / app +ADD tests / app/tests # User defined requirements # RUN make init diff --git a/README.md b/README.md index 21403f953..5557a25e4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Viewing Party -## Skills Assessed +# Skills Assessed Solving problems with... @@ -10,7 +10,7 @@ Solving problems with... - Nested loops - Nested data structures -## Goal +# Goal You and your friends enjoy watching things together online. Of course, everyone has seen different things, has different favorites, and different things they want to watch. @@ -18,7 +18,7 @@ You've been using a spreadsheet to compare everyone's watched list, favorites li For this project, you will be given some data structure that represents the things you've watched, favorited, and want to watch. The directions below will lead you to create a series of functions. These functions will modify the data, and implement features like adding and removing things between different lists. Other features include creating recommendations! -## One-Time Project Setup +# One-Time Project Setup Follow these directions once, a the beginning of your project: @@ -28,7 +28,7 @@ Follow these directions once, a the beginning of your project: $ cd ~/Developer/projects ``` -2. In Github click on the "Fork" button in github and fork the repository to your Github account. This will make a copy of the project in your github account. +2. In Github click on the "Fork" button in github and fork the repository to your Github account. This will make a copy of the project in your github account. ![Fork Button](images/fork.png) @@ -49,7 +49,7 @@ $ cd viewing-party 5. Create a virtual environment named `venv` for this project: ```bash -$ python3 -m venv venv +$ python3 - m venv venv ``` 6. Activate this environment: @@ -60,26 +60,26 @@ $ source venv/bin/activate Verify that you're in a python3 virtual environment by running: -- `$ python --version` should output a Python 3 version -- `$ pip --version` should output that it is working with Python 3 +- `$ python - -version` should output a Python 3 version +- `$ pip - -version` should output that it is working with Python 3 6. Install dependencies once at the beginning of this project with ```bash # Must be in activated virtual environment -$ pip install -r requirements.txt +$ pip install - r requirements.txt ``` Summary of one-time project setup: -- [ ] `cd` into your `projects` folder -- [ ] Clone the project onto your machine -- [ ] `cd` into the `viewing-party` folder -- [ ] Create the virtual environment `venv` -- [ ] Activate the virtual environment `venv` -- [ ] Install the dependencies with `pip` +- [] `cd` into your `projects` folder +- [] Clone the project onto your machine +- [] `cd` into the `viewing-party` folder +- [] Create the virtual environment `venv` +- [] Activate the virtual environment `venv` +- [] Install the dependencies with `pip` -## Project Development Workflow +# Project Development Workflow 1. When you want to begin work on this project, ensure that your virtual environment is activated: @@ -90,8 +90,8 @@ $ source venv/bin/activate 2. Find the test file that contains the test you want to run. Ensure that the test(s) you want to run isn't skipped. - Check the `tests` folder, and find the test file you want to run - - In that test file, read through each test case - - Remove all lines that contain `@pytest.mark.skip()` + - In that test file, read through each test case + - Remove all lines that contain `@ pytest.mark.skip()` 3. Run the tests! @@ -120,7 +120,7 @@ $ pytest $ deactivate ``` -## Details About How to Run Tests +# Details About How to Run Tests Run all unskipped tests that exist in this project with: @@ -129,11 +129,11 @@ Run all unskipped tests that exist in this project with: $ pytest ``` -If you want to see any `print` statements print to the console, add `-s` to the end of any `pytest` command: +If you want to see any `print` statements print to the console, add `- s` to the end of any `pytest` command: ```bash # Must be in activated virtual environment -$ pytest -s +$ pytest - s ``` If you want to run all unskipped tests that exist in one file, use: @@ -145,7 +145,7 @@ $ pytest tests/test_file_name.py ... where `test_file_name.py` is replaced with the correct test file name. -## Project Write-Up: How to Complete and Submit +# Project Write-Up: How to Complete and Submit The goal of this project is to write code in `main.py` so that as many of the tests pass as possible. @@ -159,13 +159,14 @@ To complete this project, use the above workflow and follow these steps: At submission time, no matter where you are, submit the project via Learn. -## Project Directions +# Project Directions -This project is designed such that one could puzzle together how to implement this project without many directions. Being able to read tests to understand what is expected of our program is a skill that needs to be developed; programmers often take years to develop this skill competently. +This project is designed such that one could puzzle together how to implement this project without many directions. Being able to read tests to understand what is expected of our program is a skill that needs to be developed +programmers often take years to develop this skill competently. When our test failures leave us confused and stuck, let's use the detailed project requirements below. -### Wave 1 +# Wave 1 1. The first four tests are about a `create_movie()` function. @@ -173,9 +174,9 @@ In `main.py`, there should be a function named `create_movie`. This function sho - take three parameters: `title`, `genre`, `rating` - If those there attributes are truthy, then return a dictionary. This dictionary should... - - Have three key-value pairs, with specific keys - - The three keys should be `"title"`, `"genre"`, and `"rating"` - - The values of these key-value pairs should be appropriate values + - Have three key-value pairs, with specific keys + - The three keys should be `"title"`, `"genre"`, and `"rating"` + - The values of these key-value pairs should be appropriate values - If `title` is falsy, `genre` is falsy, or `rating` is falsy, this function should return `None` 2. The next test is about a `add_to_watched()` function. @@ -183,16 +184,16 @@ In `main.py`, there should be a function named `create_movie`. This function sho In `main.py`, there should be a function named `add_to_watched`. This function should... - take two parameters: `user_data`, `movie` - - the value of `user_data` will be a dictionary with a key `"watched"`, and a value `[]` - - This represents that the user has no movies in their watched list - - the value of `movie` will be a dictionary in this format: - - ```python - { - "title": "Title A", - "genre": "Horror", - "rating": 3.5 - } - ``` + - the value of `user_data` will be a dictionary with a key `"watched"`, and a value `[]` + - This represents that the user has no movies in their watched list + - the value of `movie` will be a dictionary in this format: + - ```python + { + "title": "Title A", + "genre": "Horror", + "rating": 3.5 + } + ``` - add the `movie` to the `"watched"` list inside of `user_data` - return the `user_data` @@ -201,16 +202,16 @@ In `main.py`, there should be a function named `add_to_watched`. This function s In `main.py`, there should be a function named `add_to_watchlist`. This function should... - take two parameters: `user_data`, `movie` - - the value of `user_data` will be a dictionary with a key `"watchlist"`, and a value `[]` - - This represents that the user has no movies in their watchlist - - the value of `movie` will be a dictionary in this format: - - ```python - { - "title": "Title A", - "genre": "Horror", - "rating": 3.5 - } - ``` + - the value of `user_data` will be a dictionary with a key `"watchlist"`, and a value `[]` + - This represents that the user has no movies in their watchlist + - the value of `movie` will be a dictionary in this format: + - ```python + { + "title": "Title A", + "genre": "Horror", + "rating": 3.5 + } + ``` - add the `movie` to the `"watchlist"` list inside of `user_data` - return the `user_data` @@ -219,28 +220,28 @@ In `main.py`, there should be a function named `add_to_watchlist`. This function In `main.py`, there should be a function named `watch_movie`. This function should... - take two parameters: `user_data`, `title` - - the value of `user_data` will be a dictionary with a `"watchlist"` and a `"watched"` - - This represents that the user has a watchlist and a list of watched movies - - the value of `title` will be a string - - This represents the title of the movie the user has watched + - the value of `user_data` will be a dictionary with a `"watchlist"` and a `"watched"` + - This represents that the user has a watchlist and a list of watched movies + - the value of `title` will be a string + - This represents the title of the movie the user has watched - If the title is in a movie in the user's watchlist: - - remove that movie from the watchlist - - add that movie to watched - - return the `user_data` + - remove that movie from the watchlist + - add that movie to watched + - return the `user_data` - If the title is not a movie in the user's watchlist: - - return the `user_data` + - return the `user_data` -### Wave 2 +# Wave 2 1. The first two tests are about a `get_watched_avg_rating()` function. In `main.py`, there should be a function named `get_watched_avg_rating`. This function should... - take one parameter: `user_data` - - the value of `user_data` will be a dictionary with a `"watched"` list of movie dictionaries - - This represents that the user has a list of watched movies + - the value of `user_data` will be a dictionary with a `"watched"` list of movie dictionaries + - This represents that the user has a list of watched movies - Calculate the average rating of all movies in the watched list - - The average rating of an empty watched list is `0.0` + - The average rating of an empty watched list is `0.0` - return the average rating 2. The next two tests are about a `get_most_watched_genre()` function. @@ -248,24 +249,24 @@ In `main.py`, there should be a function named `get_watched_avg_rating`. This fu In `main.py`, there should be a function named `get_most_watched_genre`. This function should... - take one parameter: `user_data` - - the value of `user_data` will be a dictionary with a `"watched"` list of movie dictionaries. Each movie dictionary has a key `"genre"`. - - This represents that the user has a list of watched movies. Each watched movie has a genre. - - The values of `"genre"` is a string. + - the value of `user_data` will be a dictionary with a `"watched"` list of movie dictionaries. Each movie dictionary has a key `"genre"`. + - This represents that the user has a list of watched movies. Each watched movie has a genre. + - The values of `"genre"` is a string. - Determine which genre is most frequently occurring in the watched list - return the genre that is the most frequently watched -### Wave 3 +# Wave 3 1. The first two tests are about a `get_unique_watched()` function. In `main.py`, there should be a function named `get_unique_watched`. This function should... - take one parameter: `user_data` - - the value of `user_data` will be a dictionary with a `"watched"` list of movie dictionaries, and a `"friends"` - - This represents that the user has a list of watched movies and a list of friends - - The value of `"friends"` is a list - - Each item in `"friends"` is a dictionary. This dictionary has a key `"watched"`, which has a list of movie dictionaries. - - Each movie dictionary has a `"title"`. + - the value of `user_data` will be a dictionary with a `"watched"` list of movie dictionaries, and a `"friends"` + - This represents that the user has a list of watched movies and a list of friends + - The value of `"friends"` is a list + - Each item in `"friends"` is a dictionary. This dictionary has a key `"watched"`, which has a list of movie dictionaries. + - Each movie dictionary has a `"title"`. - Consider the movies that the user has watched, and consider the movies that their friends have watched. Determine which movies the user has watched, but none of their friends have watched. - Return a list of dictionaries, that represents a list of movies @@ -274,31 +275,31 @@ In `main.py`, there should be a function named `get_unique_watched`. This functi In `main.py`, there should be a function named `get_friends_unique_watched`. This function should... - take one parameter: `user_data` - - the value of `user_data` will be a dictionary with a `"watched"` list of movie dictionaries, and a `"friends"` - - This represents that the user has a list of watched movies and a list of friends - - The value of `"friends"` is a list - - Each item in `"friends"` is a dictionary. This dictionary has a key `"watched"`, which has a list of movie dictionaries. - - Each movie dictionary has a `"title"`. + - the value of `user_data` will be a dictionary with a `"watched"` list of movie dictionaries, and a `"friends"` + - This represents that the user has a list of watched movies and a list of friends + - The value of `"friends"` is a list + - Each item in `"friends"` is a dictionary. This dictionary has a key `"watched"`, which has a list of movie dictionaries. + - Each movie dictionary has a `"title"`. - Consider the movies that the user has watched, and consider the movies that their friends have watched. Determine which movies at least one of the user's friends have watched, but the user has not watched. - Return a list of dictionaries, that represents a list of movies -### Wave 4 +# Wave 4 1. There are two tests about a `get_available_recs` function Create a function named `get_available_recs` - takes one parameter: `user_data` - - `user_data` will have a field `"subscriptions"`. The value of `"subscriptions"` is a list of strings - - This represents the names of streaming services that the user has access to - - each friend in `"friends"` has a watched list. Each movie in the watched list has a `"host"`, which a string that says what streaming service it's hosted on + - `user_data` will have a field `"subscriptions"`. The value of `"subscriptions"` is a list of strings + - This represents the names of streaming services that the user has access to + - each friend in `"friends"` has a watched list. Each movie in the watched list has a `"host"`, which a string that says what streaming service it's hosted on - Determine a list of recommended movies. A movie should be added to this list if and only if: - - The user has not watched it - - At least one of the user's friends has watched - - The `"host"` of the movie is a service that is in the user's `"subscriptions"` + - The user has not watched it + - At least one of the user's friends has watched + - The `"host"` of the movie is a service that is in the user's `"subscriptions"` - Return the list of recommended movies -### Wave 5 +# Wave 5 1. There are four tests about a `get_new_rec_by_genre` function @@ -306,9 +307,9 @@ Create a function named `get_new_rec_by_genre` - takes one parameter: `user_data` - Consider the user's most frequently watched genre. Then, determine a list of recommended movies. A movie should be added to this list if and only if: - - The user has not watched it - - At least one of the user's friends has watched - - The `"genre"` of the movie is the same as the user's most frequent genre + - The user has not watched it + - At least one of the user's friends has watched + - The `"genre"` of the movie is the same as the user's most frequent genre - Return the list of recommended movies 1. There is also one test about a `get_rec_from_favorites` function @@ -316,9 +317,9 @@ Create a function named `get_new_rec_by_genre` Create a function named `get_rec_from_favorites` - takes one parameter: `user_data` - - `user_data` will have a field `"favorites"`. The value of `"favorites"` is a list of movie dictionaries - - This represents the user's favorite movies + - `user_data` will have a field `"favorites"`. The value of `"favorites"` is a list of movie dictionaries + - This represents the user's favorite movies - Consider the user's most frequently watched genre. Then, determine a list of recommended movies. A movie should be added to this list if and only if: - - The movie is in the user's `"favorites"` - - None of the user's friends have watched it + - The movie is in the user's `"favorites"` + - None of the user's friends have watched it - Return the list of recommended movies diff --git a/playground.py b/playground.py new file mode 100644 index 000000000..7c059b5b6 --- /dev/null +++ b/playground.py @@ -0,0 +1,84 @@ +from viewing_party.main import * + +sonyas_data = { + "watched": [ + { + "title": "Title A", + "genre": "Intrigue" + }, + { + "title": "Title B", + "genre": "Intrigue" + }, + { + "title": "Title C", + "genre": "Fantasy" + } + ], + "friends": [ + { + "watched": [ + { + "title": "Title D", + "genre": "Intrigue" + } + ] + }, + { + "watched": [ + { + "title": "Title C", + "genre": "Fantasy" + }, + { + "title": "Title E", + "genre": "Intrigue" + } + ] + } + ] +} + + +recommendations = get_new_rec_by_genre(sonyas_data) + +#print(recommendations) + +# testing get most watched genre +# janes_data = { +# "watched": [ +# { +# "title": "Title A", +# "genre": "Fantasy" +# }, +# { +# "title": "Title B", +# "genre": "Intrigue" +# }, +# { +# "title": "Title C", +# "genre": "Intrigue" +# }, +# { +# "title": "Title D", +# "genre": "Fantasy" +# }, +# { +# "title": "Title E", +# "genre": "Intrigue" +# }, +# ] +# } +janes_data = { + "watched": [] + } + + +genre_dict = calculate_genre_freq(janes_data) + + +most_watched = get_most_watched_genre(janes_data) +print(most_watched) + +# watched = [] +# print(bool(watched)) diff --git a/requirements.txt b/requirements.txt index 77ffa3f4a..da6dc4731 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ -attrs==20.3.0 -iniconfig==1.1.1 -packaging==20.8 -pluggy==0.13.1 -py==1.10.0 -pyparsing==2.4.7 -pytest==6.2.1 -toml==0.10.2 +attrs == 20.3.0 +iniconfig == 1.1.1 +packaging == 20.8 +pluggy == 0.13.1 +py == 1.10.0 +pyparsing == 2.4.7 +pytest == 6.2.1 +toml == 0.10.2 diff --git a/test.sh b/test.sh index 55b033e90..e079f8a60 100644 --- a/test.sh +++ b/test.sh @@ -1 +1 @@ -pytest \ No newline at end of file +pytest diff --git a/tests/test_wave_01.py b/tests/test_wave_01.py index d2c0c834d..ba2d6ce05 100644 --- a/tests/test_wave_01.py +++ b/tests/test_wave_01.py @@ -18,7 +18,7 @@ def test_create_movie_all_params_valid_returns_movie(): assert new_movie["rating"] is 3.5 -def test_create_movie_no_title_returns_none(): +def test_create_movie_no_title_returns_none(): # Arrange movie_title = None genre = "Horror" diff --git a/tests/test_wave_05.py b/tests/test_wave_05.py index 5c8415834..dc1860f2e 100644 --- a/tests/test_wave_05.py +++ b/tests/test_wave_05.py @@ -196,7 +196,7 @@ def test_get_rec_from_favorites_returns_expected_list_from_valid_input(): # Act recommendations = get_rec_from_favorites(sonyas_data) - + # Assert assert len(recommendations) is 1 assert {"title": "Title A"} in recommendations diff --git a/viewing_party/main.py b/viewing_party/main.py index e69de29bb..8b972db39 100644 --- a/viewing_party/main.py +++ b/viewing_party/main.py @@ -0,0 +1,181 @@ +### --------------- WAVE 1 --------------- ### + +def create_movie(movie_title, genre, rating): + # take in three parameters title, genre, rating + new_movie = { + "title": movie_title, + "genre": genre, + "rating": rating + } + # if title, genre, OR rating is falsy, return None + for values in new_movie.values(): + if values == None: + return None + # step 3: if the attributes are truthy, return dictionary + else: + return new_movie + + +def add_to_watched(user_data, movie): + # access the value of "watched" list and assign variable + watched_list = user_data["watched"] + + # append movie to "watched" list + watched_list.append(movie) + return user_data + + +def add_to_watchlist(user_data, movie): + # just repeat what you did in add_to_watched + watchlist = user_data["watchlist"] + watchlist.append(movie) + return user_data + +def watch_movie(user_data, title): + # iterate thru the movies in "watchlist" list + for movie in user_data['watchlist']: + # check if the value of movie["title"] matches the paremeter title + if title == movie['title']: + # then add to "watched" list and remove from "watchedlist" list + user_data['watched'].append(movie) + user_data['watchlist'].remove(movie) + return user_data + +### --------------- WAVE 2 --------------- ### + +def get_watched_avg_rating(user_data): + watched_list = user_data["watched"] + rating_list = [] + + # loop to make list that captures only the ratings + for movie in watched_list: + rating_list.append(movie["rating"]) + # handle for zero division error + if len(rating_list) == 0: + avg_rating = 0.0 + return avg_rating + else: + avg_rating = sum(rating_list)/len(rating_list) + return avg_rating + +def calculate_genre_freq(user_data): + ''' + helper function to calculate genre frequency + ''' + + # dictionary where the keys are the genre "fanatasy" + genre_freq_dict = {} + watch_list = user_data["watched"] + + for movie in watch_list: + genre = movie["genre"] + # go thru watch_list and check if that genre is arleady a key + # if its already in dictionary add 1 to it + if genre in genre_freq_dict.keys(): + genre_freq_dict[genre] += 1 + else: + genre_freq_dict[genre] = 1 + return genre_freq_dict + +def get_most_watched_genre(user_data): + #call calculate_genre_freq to get genre_freq_dict + genre_freq_dict = calculate_genre_freq(user_data) + #initalize highest to 0 + highest = 0 + most_watched_genre = None + #iterate through the genre_freq_dict + for genre, freq in genre_freq_dict.items(): + #check if the value (genre_freq_dict[genre]) is higher than the current value stored in highest + if freq > highest: #value int + # if it is assign highest_genre to current genre + # and replace highest with the freq + highest = freq #value int + most_watched_genre = genre + return most_watched_genre + + +### --------------- WAVE 3 --------------- ### + +def get_unique_watched(user_data): + + user_movies = user_data["watched"] # list of the movies (dict) + friends_list = user_data["friends"] # list of dict + + # make a set for user watched and friend watched + user_set = set() + friend_set = set() + + for watched_dict in friends_list: + for movie in watched_dict["watched"]: + friend_set.add(movie["title"]) + + for movie in user_movies: + user_set.add(movie["title"]) + + # compare the sets to find the movies the user has watched but the friend hasnt + user_unique_movies_set = user_set.difference(friend_set) + + # put the movies into a list of dictionaries (each dict is movie) + user_unique_movies_list = [] + for movie_name in user_unique_movies_set: + movie = dict(title=movie_name) + user_unique_movies_list.append(movie) + return user_unique_movies_list + + +def get_friends_unique_watched(user_data): + # initiate empty list to capture friend's unique movies + unique_movies = [] + # iterate through the "friends" lists of dictionaries + for friends_watched in user_data["friends"]: + # iterate through the movies in "watched" dictionary + for movie in friends_watched["watched"]: + # check if movie is not in "watched" list and not already in unique_movies list + if movie not in user_data["watched"] and movie not in unique_movies: + unique_movies.append(movie) + return unique_movies + +### --------------- WAVE 4 --------------- ### + +def get_available_recs(user_data): + # call get_friends_unique_watched to use as helper function + unique_movies = get_friends_unique_watched(user_data) + # recommended movies are the unique ones that have a host in the users subscription list + recommended_movies = [] + + # iterate thru unique_movies + for movie in unique_movies: + # append to recommended movies if the movie ["host"] is in user_data["subscriptions"] + if movie["host"] in user_data["subscriptions"]: + recommended_movies.append(movie) + return recommended_movies + +### --------------- WAVE 5 --------------- ### + +def get_new_rec_by_genre(user_data): + # invoke functions to get friends unique movies and users fav genre + friends_unique_movies = get_friends_unique_watched(user_data) + users_favorite_genre = get_most_watched_genre(user_data) + + # determine list of recommended movies + recommended_movies_genre = [] + for movie in friends_unique_movies: + if movie["genre"] == users_favorite_genre: + recommended_movies_genre.append(movie) + return recommended_movies_genre + + +def get_rec_from_favorites(user_data): + + # make an empty set for friends watched movies + friends_watched_movies = set() + recs = [] + for friend in user_data["friends"]: + for movie in friend["watched"]: + friends_watched_movies.add(movie["title"]) + + # list of users favorites + for movie in user_data["favorites"]: + if movie["title"] not in friends_watched_movies: + recs.append(movie) + return recs \ No newline at end of file