Skip to content

Commit

Permalink
Merge pull request #7 from COSC381-2023Fall/luna/3-feature-get-a-full…
Browse files Browse the repository at this point in the history
…-description-of-a-movie-review

Get a full description of a movie review
  • Loading branch information
Luna-Jia authored Dec 16, 2023
2 parents 5d6276e + ac9b739 commit 8b93551
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 48 deletions.
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,19 @@
6. To run the fast API application, use the following command:
`uvicorn main:app --reload`

7. In the browser, go to `http://127.0.0.1:8000`, the webpage will return a JSON response:
7. In the browser, go to [http://127.0.0.1:8000](http://127.0.0.1:8000), the webpage will return a JSON response:
`{"Hello": "world"}`

8. Run test, in termial, type `pytest --cov=.`

9. # run `youtube.py` to see 10 reviews for movie "Inception", in terminal, type:
`python youtube.py "Inception"`

10. To run the fast API application, and search for 10 reviews/comments for a movie, in the web browser, type:
`http://127.0.0.1:8000/moviereviews/<movie name>`. For example, to search reviews for movie Inception, type:
`http://127.0.0.1:8000/moviereviews/Inception`
10. To run the fast API application, and search for 10 reviews/comments for a movie, in the web browser, type:`http://127.0.0.1:8000/moviereviews/<movie name>`. For example, to search reviews for movie Inception, go to:
[http://127.0.0.1:8000/moviereviews/Inception](http://127.0.0.1:8000/moviereviews/Inception)

11. To retrieve the description of a specific video by its ID, in terminal, type:
`python youtube.py --description "VIDEO_ID"`

12. To test for different API endpoints, go to [FastAPI Doc](http://127.0.0.1:8000/docs) in your browser.

11 changes: 8 additions & 3 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Union
from fastapi import FastAPI
from youtube import youtube_search_reviews
from fastapi import FastAPI, HTTPException
from youtube import youtube_search_reviews, get_video_description

app = FastAPI()

Expand All @@ -12,4 +12,9 @@ def read_root():
@app.get("/moviereviews/{movie_name}")
def get_movie_reviews(movie_name: str):
reviews = youtube_search_reviews(movie_name)
return reviews
return reviews

@app.get("/descriptions/{video_id}")
def get_description(video_id: str):
description = get_video_description(video_id)
return {"video_id": video_id, "description": description}
30 changes: 15 additions & 15 deletions test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,28 @@ def test_read_root():
assert response.status_code == 200
assert response.json() == {"Hello": "world"}

# Mock the youtube_search_reviews function to control its output
@patch('main.youtube_search_reviews')
def test_get_movie_reviews(mock_youtube_search):
# Define the mock return value
mock_youtube_search.return_value = [
{
'videoId': 'test_video_id_1',
'title': 'Test Movie Review Title 1',
'description': 'Test Movie Review Description 1'
},
{'videoId': 'test_video_id_1', 'title': 'Test Movie Review Title 1', 'description': 'Test Movie Review Description 1'},
# Add more mock video data as needed
]

# Make a request to the test client
response = client.get("/moviereviews/Inception")

# Assert that the status code is 200 OK
assert response.status_code == 200

# Assert that the returned JSON matches the mock return value
assert response.json() == mock_youtube_search.return_value

# Assert that the mock function was called with the correct arguments
mock_youtube_search.assert_called_once_with("Inception")

# New test for /descriptions/{video_id}
@patch('main.get_video_description')
def test_get_video_description(mock_get_description):
mock_video_id = "test_video_id"
mock_description = "This is a test description for the video."
mock_get_description.return_value = mock_description

response = client.get(f"/descriptions/{mock_video_id}")

assert response.status_code == 200
assert response.json() == {"video_id": mock_video_id, "description": mock_description}
mock_get_description.assert_called_once_with(mock_video_id)


57 changes: 45 additions & 12 deletions test_youtube.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,24 @@
SAMPLE_API_RESPONSE = {
"items": [
{
"id": {"kind": "youtube#video", "videoId": "sample_video_id_1"},
"id": {"kind": "youtube#video", "videoId": "RNjdepthRrg"},
"snippet": {
"title": "Sample Review 1",
"description": "This is the description for Sample Review 1"
"title": "Inception - Movie Review by Chris Stuckmann",
"description": "By request, another classic review from my previous channel, this time, it's Christopher Nolan's Inception."
},
},
]
}

MOCK_VIDEO_DESCRIPTION_RESPONSE = {
"items": [
{
"kind": "youtube#video",
"id": "RNjdepthRrg",
"snippet": {
"description": "By request, another classic review from my previous channel, this time, it's Christopher Nolan's Inception."
},
},

]
}

Expand All @@ -26,19 +37,41 @@ def test_youtube_search_reviews(mock_build):
mock_build.return_value = mock_service
mock_service.search().list().execute.return_value = SAMPLE_API_RESPONSE

results = youtube_search_reviews("Sample Movie")
results = youtube_search_reviews("Inception")

assert mock_build.called
assert len(results) == 1
assert results[0]['videoId'] == 'sample_video_id_1'
assert results[0]['title'] == 'Sample Review 1'
assert 'Sample Review 1' in results[0]['description']
assert results[0]['videoId'] == 'RNjdepthRrg'
assert results[0]['title'] == 'Inception - Movie Review by Chris Stuckmann'
assert "By request, another classic review" in results[0]['description']

def test_script_no_args():
result = subprocess.run(['python', 'youtube.py'], capture_output=True, text=True)
assert 'Usage: python youtube.py' in result.stdout or 'Usage: python youtube.py' in result.stderr
@patch('youtube.build')
def test_get_video_description(mock_build):
mock_service = Mock()
mock_build.return_value = mock_service
mock_service.videos().list().execute.return_value = MOCK_VIDEO_DESCRIPTION_RESPONSE

video_description = youtube.get_video_description("RNjdepthRrg")

assert mock_build.called
assert video_description == "By request, another classic review from my previous channel, this time, it's Christopher Nolan's Inception."
mock_service.videos().list.assert_any_call(id="RNjdepthRrg", part='snippet', maxResults=1)

# test_script_valid_input remains the same


# def test_script_no_args():
# result = subprocess.run(['python', 'youtube.py'], capture_output=True, text=True)
# assert 'Usage: python youtube.py' in result.stdout or 'Usage: python youtube.py' in result.stderr

def test_script_valid_input():
result = subprocess.run(['python', 'youtube.py', 'Inception'], capture_output=True, text=True)
assert result.returncode == 0
assert 'Inception' in result.stdout
assert 'Inception' in result.stdout

def test_script_with_description_flag():
# Simulate running the script with `--description` and a video ID
result = subprocess.run(['python', 'youtube.py', '--description', 'RNjdepthRrg'], capture_output=True, text=True)

# Check that the output is as expected (adjust according to actual expected output)
assert "By request, another classic review" in result.stdout.strip()
39 changes: 25 additions & 14 deletions youtube.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@
YOUTUBE_API_SERVICE_NAME = 'youtube'
YOUTUBE_API_VERSION = 'v3'

# defined a function "youtube_search_reviews" that only requires the query_term parameter;
def youtube_search_reviews(query_term, max_results=10): # set a default max_results to 10.
def youtube_search_reviews(query_term, max_results=10):
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=DEVELOPER_KEY)

search_response = youtube.search().list(
q=f"{query_term} review|commentary", #search query includes "review" or "commentary" by using the pipe | which acts as a logical OR in the YouTube search query.
q=f"{query_term} review|commentary",
part='id,snippet',
maxResults=max_results,
type='video',
order='relevance',
order='relevance'
).execute()

videos = []
Expand All @@ -31,15 +30,27 @@ def youtube_search_reviews(query_term, max_results=10): # set a default max_resu

return videos

if __name__ == '__main__':
if len(sys.argv) < 2:
print("Usage: python youtube.py 'QUERY_TERM'")
sys.exit(1)
def get_video_description(video_id):
youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=DEVELOPER_KEY)

query_term = sys.argv[1]
video_response = youtube.videos().list(
id=video_id,
part='snippet',
maxResults=1
).execute()

review_videos = youtube_search_reviews(query_term)
for video in review_videos:
print(f"Video ID: {video['videoId']}")
print(f"Title: {video['title']}")
print(f"Description: {video['description']}\n")
video_description = video_response.get('items', [{}])[0].get('snippet', {}).get('description', '')
return video_description

if __name__ == '__main__':
if len(sys.argv) == 3 and sys.argv[1] == '--description':
video_id = sys.argv[2]
print(get_video_description(video_id))

else:
query_term = sys.argv[1]
review_videos = youtube_search_reviews(query_term)
for video in review_videos:
print(f"Video ID: {video['videoId']}")
print(f"Title: {video['title']}")
print(f"Description: {video['description']}\n")

0 comments on commit 8b93551

Please sign in to comment.