initial commit
This commit is contained in:
		
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,8 @@
 | 
				
			|||||||
__pycache__/
 | 
					__pycache__/
 | 
				
			||||||
*.pyc
 | 
					*.pyc
 | 
				
			||||||
.env
 | 
					.env
 | 
				
			||||||
 | 
					.python-version
 | 
				
			||||||
 | 
					dist/
 | 
				
			||||||
 | 
					build/
 | 
				
			||||||
 | 
					movietimes.egg-info/
 | 
				
			||||||
 | 
					.pytest_cache/
 | 
				
			||||||
							
								
								
									
										23
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					# MovieTimes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MovieTimes is a Python-based scraper that fetches movie showtimes for a given ZIP code using Google search results.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Installation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					git clone https://github.com/yourusername/movietimes.git
 | 
				
			||||||
 | 
					cd movietimes
 | 
				
			||||||
 | 
					pip install -r requirements.txt
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					python src/showtimes_scraper.py 90210
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Testing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					pytest
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
							
								
								
									
										3
									
								
								requirements-dev.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								requirements-dev.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					pytest
 | 
				
			||||||
 | 
					setuptools
 | 
				
			||||||
 | 
					build
 | 
				
			||||||
							
								
								
									
										3
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								requirements.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					requests
 | 
				
			||||||
 | 
					beautifulsoup4
 | 
				
			||||||
 | 
					rich
 | 
				
			||||||
							
								
								
									
										17
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								setup.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					from setuptools import setup, find_packages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					setup(
 | 
				
			||||||
 | 
					    name="movietimes",
 | 
				
			||||||
 | 
					    version="1.0",
 | 
				
			||||||
 | 
					    packages=find_packages(where="src"),
 | 
				
			||||||
 | 
					    package_dir={"": "src"},
 | 
				
			||||||
 | 
					    install_requires=[
 | 
				
			||||||
 | 
					        "requests",
 | 
				
			||||||
 | 
					        "rich"
 | 
				
			||||||
 | 
					    ],
 | 
				
			||||||
 | 
					    entry_points={
 | 
				
			||||||
 | 
					        "console_scripts": [
 | 
				
			||||||
 | 
					            "movietimes=movietimes.cli:main",
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										1
									
								
								src/movietimes/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/movietimes/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					# src/__init__.py
 | 
				
			||||||
							
								
								
									
										4
									
								
								src/movietimes/cli.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/movietimes/cli.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					from .movie_showtimes import main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
							
								
								
									
										55
									
								
								src/movietimes/movie_showtimes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/movietimes/movie_showtimes.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					import requests
 | 
				
			||||||
 | 
					import time
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					from rich.console import Console
 | 
				
			||||||
 | 
					from rich.table import Table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SERPAPI_SHOWTIMES_URL = "https://serpapi.com/search"
 | 
				
			||||||
 | 
					SERPAPI_API_KEY = os.getenv("SERPAPI_API_KEY", "your_serpapi_api_key")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					default_theater_query = "cinemark century boulder"
 | 
				
			||||||
 | 
					default_location = "boulder, colorado, united states"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					console = Console()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def get_movie_showtimes():
 | 
				
			||||||
 | 
					    params = {
 | 
				
			||||||
 | 
					        'q': default_theater_query,
 | 
				
			||||||
 | 
					        'hl': 'en',
 | 
				
			||||||
 | 
					        'gl': 'us',
 | 
				
			||||||
 | 
					        'location': default_location,
 | 
				
			||||||
 | 
					        'api_key': SERPAPI_API_KEY
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    response = requests.get(SERPAPI_SHOWTIMES_URL, params=params)
 | 
				
			||||||
 | 
					    if response.status_code == 200:
 | 
				
			||||||
 | 
					        data = response.json()
 | 
				
			||||||
 | 
					        return data.get('showtimes', [])
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        raise ValueError("Could not fetch movie showtimes from SerpApi.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def display_showtimes(showtimes):
 | 
				
			||||||
 | 
					    if not showtimes:
 | 
				
			||||||
 | 
					        console.print("[bold red]No showtimes available.[/bold red]")
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    for day_data in showtimes:
 | 
				
			||||||
 | 
					        day = day_data.get("day", "Unknown Day")
 | 
				
			||||||
 | 
					        console.print(f"\n[bold cyan]{day}[/bold cyan]")
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for movie in day_data.get("movies", []):
 | 
				
			||||||
 | 
					            table = Table(title=f"{movie['name']}", show_header=True, header_style="bold magenta")
 | 
				
			||||||
 | 
					            table.add_column("Type", style="dim")
 | 
				
			||||||
 | 
					            table.add_column("Times", justify="right")
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            for showing in movie.get("showing", []):
 | 
				
			||||||
 | 
					                table.add_row(showing["type"], ", ".join(showing["time"]))
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            console.print(table)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main():
 | 
				
			||||||
 | 
					    showtimes = get_movie_showtimes()
 | 
				
			||||||
 | 
					    display_showtimes(showtimes)
 | 
				
			||||||
 | 
					    time.sleep(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == '__main__':
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
							
								
								
									
										1
									
								
								tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					# Make tests a package
 | 
				
			||||||
							
								
								
									
										69
									
								
								tests/test_movie_showtimes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								tests/test_movie_showtimes.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					import pytest
 | 
				
			||||||
 | 
					import requests
 | 
				
			||||||
 | 
					from unittest.mock import patch
 | 
				
			||||||
 | 
					from src.movie_showtimes import get_movie_showtimes, display_showtimes
 | 
				
			||||||
 | 
					from io import StringIO
 | 
				
			||||||
 | 
					from rich.console import Console
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture
 | 
				
			||||||
 | 
					def mock_successful_response():
 | 
				
			||||||
 | 
					    """Mock a successful API response."""
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        "showtimes": [
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                "day": "Today",
 | 
				
			||||||
 | 
					                "movies": [
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        "name": "Dune",
 | 
				
			||||||
 | 
					                        "showing": [
 | 
				
			||||||
 | 
					                            {"time": ["12:30pm", "4:00pm"], "type": "Standard"},
 | 
				
			||||||
 | 
					                            {"time": ["1:30pm", "5:30pm"], "type": "IMAX"}
 | 
				
			||||||
 | 
					                        ]
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@pytest.fixture
 | 
				
			||||||
 | 
					def mock_failed_response():
 | 
				
			||||||
 | 
					    """Mock a failed API response."""
 | 
				
			||||||
 | 
					    return {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@patch("requests.get")
 | 
				
			||||||
 | 
					def test_get_movie_showtimes_success(mock_get, mock_successful_response):
 | 
				
			||||||
 | 
					    """Test fetching movie showtimes successfully from SerpApi."""
 | 
				
			||||||
 | 
					    mock_get.return_value.status_code = 200
 | 
				
			||||||
 | 
					    mock_get.return_value.json.return_value = mock_successful_response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    showtimes = get_movie_showtimes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert len(showtimes) == 1
 | 
				
			||||||
 | 
					    assert showtimes[0]["day"] == "Today"
 | 
				
			||||||
 | 
					    assert showtimes[0]["movies"][0]["name"] == "Dune"
 | 
				
			||||||
 | 
					    assert showtimes[0]["movies"][0]["showing"][0]["type"] == "Standard"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@patch("requests.get")
 | 
				
			||||||
 | 
					def test_get_movie_showtimes_failure(mock_get, mock_failed_response):
 | 
				
			||||||
 | 
					    """Test handling of API failure (non-200 response)."""
 | 
				
			||||||
 | 
					    mock_get.return_value.status_code = 500
 | 
				
			||||||
 | 
					    mock_get.return_value.json.return_value = mock_failed_response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    with pytest.raises(ValueError, match="Could not fetch movie showtimes from SerpApi."):
 | 
				
			||||||
 | 
					        get_movie_showtimes()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@patch("sys.stdout", new_callable=StringIO)
 | 
				
			||||||
 | 
					def test_display_showtimes_output(mock_stdout, mock_successful_response):
 | 
				
			||||||
 | 
					    """Test console output formatting of display_showtimes."""
 | 
				
			||||||
 | 
					    console = Console(file=mock_stdout)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    display_showtimes(mock_successful_response["showtimes"])
 | 
				
			||||||
 | 
					    output = mock_stdout.getvalue()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert "Today" in output
 | 
				
			||||||
 | 
					    assert "Dune" in output
 | 
				
			||||||
 | 
					    assert "Standard" in output
 | 
				
			||||||
 | 
					    assert "12:30pm, 4:00pm" in output
 | 
				
			||||||
 | 
					    assert "IMAX" in output
 | 
				
			||||||
 | 
					    assert "1:30pm, 5:30pm" in output
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Reference in New Issue
	
	Block a user