diff --git a/db.sqlite3 b/db.sqlite3 index 701d358..b18f12a 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/movies/tests.py b/movies/tests.py index cb094b4..423910e 100644 --- a/movies/tests.py +++ b/movies/tests.py @@ -1,4 +1,5 @@ from django.test import TestCase +from django.core.urlresolvers import reverse from .factories import ActorFactory, MovieFactory from .models import Movie @@ -22,14 +23,188 @@ def test_movie_with_actors(self): self.assertEqual(actor.movies.all()[0], movie) +class PaginatedMovieTestCase(TestCase): + + def test_url_without_page_returns_most_recent(self): + + [MovieFactory() for _ in range(140)] + movies = Movie.objects.all().order_by('-release_date', 'title')[:5] + movie_titles = [m.title for m in movies] + + response_page1 = self.client.get(reverse('movies:recent-movies')+'?page=').json() + self.assertListEqual(response_page1, movie_titles) + + response_page2 = self.client.get(reverse('movies:recent-movies')).json() + self.assertListEqual(response_page2, movie_titles) + + def test_url_with_page_offsets_to_correct_page(self): + + [MovieFactory() for _ in range(140)] + movies = Movie.objects.all().order_by('-release_date', 'title')[20:25] + movie_titles = [m.title for m in movies] + + response_page = self.client.get(reverse('movies:recent-movies')+'?page=5').json() + self.assertListEqual(response_page, movie_titles) + + def test_url_with_page_out_of_range_display_404(self): + + [MovieFactory() for _ in range(100)] + + response = self.client.get(reverse('movies:recent-movies')+'?page=21') + self.assertEqual(response.status_code, 404) + + +class RecentMoviesTestCase(TestCase): + + def test_max_number_of_movies_returned_are_5(self): + + [MovieFactory() for _ in range(10)] + response = self.client.get(reverse('movies:recent-movies')) + response_json = response.json() + self.assertLessEqual(len(response_json), 5) + + [MovieFactory() for _ in range(3)] + response = self.client.get(reverse('movies:recent-movies')) + response_json = response.json() + self.assertLessEqual(len(response_json), 5) + + def test_recent_movies_returned_having_same_ratings_are_sorted_by_title(self): + + movies = [ + MovieFactory(release_date='2013-01-05'), + MovieFactory(release_date='2014-02-27', title='Spiderman'), + MovieFactory(release_date='2014-03-15', title='Superman'), + MovieFactory(release_date='2015-08-23'), + MovieFactory(release_date='2017-02-05'), + ] + movie_titles = [m.title for m in reversed(movies)] + + response = self.client.get(reverse('movies:recent-movies')) + response_json = response.json() + + self.assertListEqual(movie_titles, response_json) + + def test_recent_movies_returned_are_sorted_by_ratings(self): + + movies = [ + MovieFactory(release_date='2013-01-05'), + MovieFactory(release_date='2014-02-27'), + MovieFactory(release_date='2014-03-15'), + MovieFactory(release_date='2015-08-23'), + MovieFactory(release_date='2017-02-05'), + ] + movie_titles = [m.title for m in reversed(movies)] + + response = self.client.get(reverse('movies:recent-movies')) + response_json = response.json() + + self.assertListEqual(movie_titles, response_json) + + def test_recent_movies_returns_a_list_of_movie_titles(self): + + movies = [MovieFactory() for _ in range(5)] + movie_titles = [m.title for m in movies] + + response = self.client.get(reverse('movies:recent-movies')) + response_json = response.json() + + for movie_title in movie_titles: + self.assertIn(movie_title, response_json) + + for movie_title in response_json: + self.assertIn(movie_title, movie_titles) + + def test_recent_movies_returns_a_list(self): + response = self.client.get(reverse('movies:recent-movies')) + response_json = response.json() + self.assertIsInstance(response_json, list) + + def test_recent_movies_returns_json(self): + response = self.client.get(reverse('movies:recent-movies')) + self.assertEqual(response['content-type'], 'application/json') + + def test_non_get_requests_return_400(self): + response = self.client.post(reverse('movies:recent-movies')) + self.assertEqual(response.status_code, 400) + + response = self.client.put(reverse('movies:recent-movies')) + self.assertEqual(response.status_code, 400) + + response = self.client.delete(reverse('movies:recent-movies')) + self.assertEqual(response.status_code, 400) + + response = self.client.patch(reverse('movies:recent-movies')) + self.assertEqual(response.status_code, 400) + + response = self.client.head(reverse('movies:recent-movies')) + self.assertEqual(response.status_code, 400) + + def test_get_best_movies_url(self): + response = self.client.get(reverse('movies:recent-movies')) + self.assertEqual(response.status_code, 200) + + class BestMoviesTestCase(TestCase): + def test_no_pagination(self): + + [MovieFactory() for _ in range(140)] + + response_page1 = self.client.get(reverse('movies:best-movies')+'?page=1').json() + response_page2 = self.client.get(reverse('movies:best-movies')+'?page=2').json() + + self.assertListEqual(response_page1, response_page2) + + def test_max_number_of_movies_returned_are_5(self): + + [MovieFactory() for _ in range(10)] + response = self.client.get(reverse('movies:best-movies')) + response_json = response.json() + self.assertLessEqual(len(response_json), 5) + + [MovieFactory() for _ in range(3)] + response = self.client.get(reverse('movies:best-movies')) + response_json = response.json() + self.assertLessEqual(len(response_json), 5) + + def test_best_movies_returned_having_same_ratings_are_sorted_by_title(self): + + movies = [ + MovieFactory(rating=5), + MovieFactory(rating=6), + MovieFactory(rating=7, title='Superman'), + MovieFactory(rating=7, title='Spiderman'), + MovieFactory(rating=9), + ] + movie_titles = [m.title for m in reversed(movies)] + + response = self.client.get(reverse('movies:best-movies')) + response_json = response.json() + + self.assertListEqual(movie_titles, response_json) + + def test_best_movies_returned_are_sorted_by_ratings(self): + + movies = [ + MovieFactory(rating=5), + MovieFactory(rating=6), + MovieFactory(rating=7), + MovieFactory(rating=8), + MovieFactory(rating=9), + ] + movie_titles = [m.title for m in reversed(movies)] + + response = self.client.get(reverse('movies:best-movies')) + response_json = response.json() + + self.assertListEqual(movie_titles, response_json) + def test_best_movies_returns_a_list_of_movie_titles(self): - # create 5 random movies - movies = [MovieFactory() for _ in range(140)] + + movies = [MovieFactory() for _ in range(5)] movie_titles = [m.title for m in movies] - response = self.client.get('/movies/best/') + response = self.client.get(reverse('movies:best-movies')) response_json = response.json() for movie_title in movie_titles: @@ -38,33 +213,31 @@ def test_best_movies_returns_a_list_of_movie_titles(self): for movie_title in response_json: self.assertIn(movie_title, movie_titles) - self.assertEqual(len(movies), len(response_json)) - def test_best_movies_returns_a_list(self): - response = self.client.get('/movies/best/') + response = self.client.get(reverse('movies:best-movies')) response_json = response.json() self.assertIsInstance(response_json, list) def test_get_best_movies_url(self): - response = self.client.get('/movies/best/') + response = self.client.get(reverse('movies:best-movies')) self.assertEqual(response.status_code, 200) def test_best_movies_returns_json(self): - response = self.client.get('/movies/best/') + response = self.client.get(reverse('movies:best-movies')) self.assertEqual(response['content-type'], 'application/json') def test_non_get_requests_return_400(self): - response = self.client.post('/movies/best/') + response = self.client.post(reverse('movies:best-movies')) self.assertEqual(response.status_code, 400) - response = self.client.put('/movies/best/') + response = self.client.put(reverse('movies:best-movies')) self.assertEqual(response.status_code, 400) - response = self.client.delete('/movies/best/') + response = self.client.delete(reverse('movies:best-movies')) self.assertEqual(response.status_code, 400) - response = self.client.patch('/movies/best/') + response = self.client.patch(reverse('movies:best-movies')) self.assertEqual(response.status_code, 400) - response = self.client.head('/movies/best/') + response = self.client.head(reverse('movies:best-movies')) self.assertEqual(response.status_code, 400) diff --git a/movies/urls.py b/movies/urls.py new file mode 100644 index 0000000..32a2802 --- /dev/null +++ b/movies/urls.py @@ -0,0 +1,9 @@ +from django.conf.urls import url + +from .views import best_movies, recent_movies + +app_name = 'movies' +urlpatterns = [ + url(r'^best/', best_movies, name="best-movies"), + url(r'^recent/', recent_movies, name="recent-movies"), +] diff --git a/movies/views.py b/movies/views.py index 133c7b8..d3094af 100644 --- a/movies/views.py +++ b/movies/views.py @@ -1,4 +1,5 @@ -from django.http import JsonResponse, HttpResponseBadRequest +from django.http import JsonResponse, HttpResponseBadRequest, HttpResponseNotFound +from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage from .models import Movie @@ -8,7 +9,28 @@ def best_movies(request): return HttpResponseBadRequest() movie_titles_list = [] - for movie in Movie.objects.all(): + for movie in Movie.objects.all().order_by('-rating', 'title')[:5]: + movie_titles_list.append(movie.title) + + return JsonResponse(movie_titles_list, safe=False) + + +def recent_movies(request): + if request.method.upper() != 'GET': + return HttpResponseBadRequest() + + paginator = Paginator(Movie.objects.all().order_by('-release_date', 'title'), 5) + page = request.GET.get('page') + + try: + movies = paginator.page(page) + except PageNotAnInteger: + movies = paginator.page(1) + except EmptyPage: + return HttpResponseNotFound() + + movie_titles_list = [] + for movie in movies: movie_titles_list.append(movie.title) return JsonResponse(movie_titles_list, safe=False) diff --git a/movietimes/urls.py b/movietimes/urls.py index e104254..a0cde59 100644 --- a/movietimes/urls.py +++ b/movietimes/urls.py @@ -1,24 +1,8 @@ -"""movietimes URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/1.11/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.conf.urls import url, include - 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) -""" -from django.conf.urls import url +from django.conf.urls import url, include from django.contrib import admin -from movies.views import best_movies urlpatterns = [ url(r'^admin/', admin.site.urls), - url(r'^movies/best/', best_movies, name="best-movies"), + url(r'^movies/', include('movies.urls')), ]