# -*- coding: utf-8 -*-

"""
tmdbsimple.movies
~~~~~~~~~~~~~~~~~
This module implements the Movies, Collections, Companies, Keywords, and
Reviews functionality of tmdbsimple.

Created by Celia Oakley on 2013-10-31.

:copyright: (c) 2013-2018 by Celia Oakley
:license: GPLv3, see LICENSE for more details
"""

from .base import TMDB

class Movies(TMDB):
    """
    Movies functionality.

    See: https://developers.themoviedb.org/3/movies
    """
    BASE_PATH = 'movie'
    URLS = {
        'info': '/{id}',
        'alternative_titles': '/{id}/alternative_titles',
        'credits': '/{id}/credits',
        'external_ids': '/{id}/external_ids',
        'images': '/{id}/images',
        'keywords': '/{id}/keywords',
        'release_dates': '/{id}/release_dates',
        'releases': '/{id}/releases',
        'videos': '/{id}/videos',
        'translations': '/{id}/translations',
        'similar_movies': '/{id}/similar_movies',
        'reviews': '/{id}/reviews',
        'lists': '/{id}/lists',
        'changes': '/{id}/changes',
        'latest': '/latest',
        'upcoming': '/upcoming',
        'now_playing': '/now_playing',
        'popular': '/popular',
        'top_rated': '/top_rated',
        'account_states': '/{id}/account_states',
        'rating': '/{id}/rating',
        'recommendations': '/{id}/recommendations'
    }

    def __init__(self, id=0):
        super(Movies, self).__init__()
        self.id = id

    def info(self, **kwargs):
        """
        Get the basic movie information for a specific movie id.

        Args:
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def alternative_titles(self, **kwargs):
        """
        Get the alternative titles for a specific movie id.

        Args:
            country: (optional) ISO 3166-1 code.
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('alternative_titles')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def credits(self, **kwargs):
        """
        Get the cast and crew information for a specific movie id.

        Args:
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('credits')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def external_ids(self, **kwargs):
        """
        Get the external ids for a specific movie id.

        Args:
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('external_ids')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def images(self, **kwargs):
        """
        Get the images (posters and backdrops) for a specific movie id.

        Args:
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any movie method.
            include_image_language: (optional) Comma separated, a valid
                                    ISO 69-1.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('images')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def keywords(self):
        """
        Get the plot keywords for a specific movie id.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('keywords')

        response = self._GET(path)
        self._set_attrs_to_values(response)
        return response

    def recommendations(self, **kwargs):
        """
        Get a list of recommended movies for a movie.

        Args:
            language: (optional) ISO 639-1 code.
            page: (optional) Minimum value of 1.  Expected value is an integer.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('recommendations')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def release_dates(self, **kwargs):
        """
        Get the release dates and certification for a specific movie id.

        Args:
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('release_dates')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def releases(self, **kwargs):
        """
        Get the release date and certification information by country for a
        specific movie id.

        Args:
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('releases')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def videos(self, **kwargs):
        """
        Get the videos (trailers, teasers, clips, etc...) for a
        specific movie id.

        Args:
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('videos')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def translations(self, **kwargs):
        """
        Get the translations for a specific movie id.

        Args:
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('translations')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def similar_movies(self, **kwargs):
        """
        Get the similar movies for a specific movie id.

        Args:
            page: (optional) Minimum value of 1.  Expected value is an integer.
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('similar_movies')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def reviews(self, **kwargs):
        """
        Get the reviews for a particular movie id.

        Args:
            page: (optional) Minimum value of 1.  Expected value is an integer.
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('reviews')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def lists(self, **kwargs):
        """
        Get the lists that the movie belongs to.

        Args:
            page: (optional) Minimum value of 1.  Expected value is an integer.
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('lists')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def changes(self, **kwargs):
        """
        Get the changes for a specific movie id.

        Changes are grouped by key, and ordered by date in descending order.
        By default, only the last 24 hours of changes are returned. The
        maximum number of days that can be returned in a single request is 14.
        The language is present on fields that are translatable.

        Args:
            start_date: (optional) Expected format is 'YYYY-MM-DD'.
            end_date: (optional) Expected format is 'YYYY-MM-DD'.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('changes')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def latest(self, **kwargs):
        """
        Get the latest movie id.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_path('latest')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def upcoming(self, **kwargs):
        """
        Get the list of upcoming movies. This list refreshes every day.
        The maximum number of items this list will include is 100.

        Args:
            page: (optional) Minimum value of 1.  Expected value is an integer.
            language: (optional) ISO 639-1 code.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_path('upcoming')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def now_playing(self, **kwargs):
        """
        Get the list of movies playing in theatres. This list refreshes
        every day. The maximum number of items this list will include is 100.

        Args:
            page: (optional) Minimum value of 1.  Expected value is an integer.
            language: (optional) ISO 639-1 code.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_path('now_playing')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def popular(self, **kwargs):
        """
        Get the list of popular movies on The Movie Database. This list
        refreshes every day.

        Args:
            page: (optional) Minimum value of 1.  Expected value is an integer.
            language: (optional) ISO 639-1 code.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_path('popular')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def top_rated(self, **kwargs):
        """
        Get the list of top rated movies. By default, this list will only
        include movies that have 10 or more votes. This list refreshes every
        day.

        Args:
            page: (optional) Minimum value of 1.  Expected value is an integer.
            language: (optional) ISO 639-1 code.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_path('top_rated')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def account_states(self, **kwargs):
        """
        This method lets users get the status of whether or not the movie has
        been rated or added to their favourite or watch lists. A valid session
        id is required.

        Args:
            session_id: see Authentication.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('account_states')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def rating(self, **kwargs):
        """
        This method lets users rate a movie. A valid session id or guest
        session id is required.

        Args:
            session_id: see Authentication.
            guest_session_id: see Authentication.
            value: Rating value.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('rating')

        payload = {
            'value': kwargs.pop('value', None),
        }

        response = self._POST(path, kwargs, payload)
        self._set_attrs_to_values(response)
        return response

class Collections(TMDB):
    """
    Collections functionality.

    See: https://developers.themoviedb.org/3/collections
    """
    BASE_PATH = 'collection'
    URLS = {
        'info': '/{id}',
        'images': '/{id}/images',
    }

    def __init__(self, id):
        super(Collections, self).__init__()
        self.id = id

    def info(self, **kwargs):
        """
        Get the basic collection information for a specific collection id.
        You can get the ID needed for this method by making a /movie/{id}
        request and paying attention to the belongs_to_collection hash.

        Movie parts are not sorted in any particular order. If you would like
        to sort them yourself you can use the provided release_date.

        Args:
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def images(self, **kwargs):
        """
        Get all of the images for a particular collection by collection id.

        Args:
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any movie method.
            include_image_language: (optional) Comma separated, a valid
            ISO 69-1.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('images')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class Companies(TMDB):
    """
    Companies functionality.

    See: https://developers.themoviedb.org/3/companies
    """
    BASE_PATH = 'company'
    URLS = {
        'info': '/{id}',
        'movies': '/{id}/movies',
    }

    def __init__(self, id=0):
        super(Companies, self).__init__()
        self.id = id

    def info(self, **kwargs):
        """
        This method is used to retrieve all of the basic information about a
        company.

        Args:
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def movies(self, **kwargs):
        """
        Get the list of movies associated with a particular company.

        Args:
            page: (optional) Minimum value of 1.  Expected value is an integer.
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any movie method.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('movies')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class Keywords(TMDB):
    """
    Keywords functionality.

    See: https://developers.themoviedb.org/3/keywords
    """
    BASE_PATH = 'keyword'
    URLS = {
        'info': '/{id}',
        'movies': '/{id}/movies',
    }

    def __init__(self, id):
        super(Keywords, self).__init__()
        self.id = id

    def info(self, **kwargs):
        """
        Get the basic information for a specific keyword id.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def movies(self, **kwargs):
        """
        Get the list of movies for a particular keyword by id.

        Args:
            page: (optional) Minimum value of 1.  Expected value is an integer.
            language: (optional) ISO 639-1 code.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('movies')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class Reviews(TMDB):
    """
    Reviews functionality.

    See: https://developers.themoviedb.org/3/reviews
    """
    BASE_PATH = 'review'
    URLS = {
        'info': '/{id}',
    }

    def __init__(self, id):
        super(Reviews, self).__init__()
        self.id = id

    def info(self, **kwargs):
        """
        Get the full details of a review by ID.

        Returns:
            A dict representation of the JSON returned from the API.
        """
        path = self._get_id_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response


# -*- coding: utf-8 -*-

"""
tmdbsimple.people
~~~~~~~~~~~~~~~~~
This module implements the People, Credits, and Jobs functionality
of tmdbsimple.

Created by Celia Oakley on 2013-10-31.

:copyright: (c) 2013-2018 by Celia Oakley
:license: GPLv3, see LICENSE for more details
"""

from .base import TMDB

class People(TMDB):
    """
    People functionality.

    See: https://developers.themoviedb.org/3/people
    """
    BASE_PATH = 'person'
    URLS = {
        'info': '/{id}',
        'movie_credits': '/{id}/movie_credits',
        'tv_credits': '/{id}/tv_credits',
        'combined_credits': '/{id}/combined_credits',
        'external_ids': '/{id}/external_ids',
        'images': '/{id}/images',
        'changes': '/{id}/changes',
        'popular': '/popular',
        'latest': '/latest',
    }

    def __init__(self, id=0):
        super(People, self).__init__()
        self.id = id

    def info(self, **kwargs):
        """
        Get the general person information for a specific id.

        Args:
            append_to_response: (optional) Comma separated, any person method.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def movie_credits(self, **kwargs):
        """
        Get the movie credits for a specific person id.

        Args:
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any person method.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('movie_credits')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def tv_credits(self, **kwargs):
        """
        Get the TV credits for a specific person id.

        Args:
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any person method.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('tv_credits')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def combined_credits(self, **kwargs):
        """
        Get the combined (movie and TV) credits for a specific person id.

        To get the expanded details for each TV record, call the /credit method
        with the provided credit_id. This will provide details about which
        episode and/or season the credit is for.

        Args:
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any person method.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('combined_credits')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def external_ids(self, **kwargs):
        """
        Get the external ids for a specific person id.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('external_ids')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def images(self, **kwargs):
        """
        Get the images for a specific person id.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('images')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def changes(self, **kwargs):
        """
        Get the changes for a specific person id.

        Changes are grouped by key, and ordered by date in descending order.
        By default, only the last 24 hours of changes are returned. The maximum
        number of days that can be returned in a single request is 14. The
        language is present on fields that are translatable.

        Args:
            start_date: (optional) Expected format is 'YYYY-MM-DD'.
            end_date: (optional) Expected format is 'YYYY-MM-DD'.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('changes')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def popular(self, **kwargs):
        """
        Get the list of popular people on The Movie Database. This list
        refreshes every day.

        Args:
            page: (optional) Minimum 1, maximum 1000.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('popular')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def latest(self, **kwargs):
        """
        Get the latest person id.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('latest')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class Credits(TMDB):
    """
    Credits functionality.

    See: https://developers.themoviedb.org/3/credits
    """
    BASE_PATH = 'credit'
    URLS = {
        'info': '/{credit_id}',
    }

    def __init__(self, credit_id):
        super(Credits, self).__init__()
        self.credit_id = credit_id

    def info(self, **kwargs):
        """
        Get the detailed information about a particular credit record. This is
        currently only supported with the new credit model found in TV. These
        ids can be found from any TV credit response as well as the tv_credits
        and combined_credits methods for people.

        The episodes object returns a list of episodes and are generally going
        to be guest stars. The season array will return a list of season
        numbers.  Season credits are credits that were marked with the
        "add to every season" option in the editing interface and are
        assumed to be "season regulars".

        Args:
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_credit_id_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class Jobs(TMDB):
    """
    Jobs functionality.

    See: https://developers.themoviedb.org/3/jobs
    """
    BASE_PATH = 'job'
    URLS = {
        'list': '/list',
    }

    def list(self, **kwargs):
        """
        Get a list of valid jobs.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('list')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response


# -*- coding: utf-8 -*-

"""
tmdbsimple.discover
~~~~~~~~~~~~~~~~~~~
This module implements the Discover functionality of tmdbsimple.

Created by Celia Oakley on 2013-10-31.

:copyright: (c) 2013-2018 by Celia Oakley
:license: GPLv3, see LICENSE for more details
"""

from .base import TMDB

class Discover(TMDB):
    """
    Discover functionality.

    See: https://developers.themoviedb.org/3/discover
    """
    BASE_PATH = 'discover'
    URLS = {
        'movie': '/movie',
        'tv': '/tv',
    }

    def movie(self, **kwargs):
        """
        Discover movies by different types of data like average rating,
        number of votes, genres and certifications.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            language: (optional) ISO 639-1 code.
            sort_by: (optional) Available options are 'vote_average.desc',
                     'vote_average.asc', 'release_date.desc', 'release_date.asc'
                     'popularity.desc', 'popularity.asc'.
            include_adult: (optional) Toggle the inclusion of adult titles.
                           Expected value is a boolean, True or False.
            year: (optional) Filter the results release dates to matches that
                  include this value. Expected value is a year.
            primary_release_year: (optional) Filter the results so that
                                  only the primary release date year has
                                  this value.  Expected value is a year.
            vote_count.gte or vote_count_gte: (optional) Only include movies
                            that are equal to, or have a vote count higher
                            than this value.  Expected value is an integer.
            vote_average.gte or vote_average_gte: (optional) Only include
                              movies that are equal to, or have a higher
                              average rating than this value.  Expected value
                              is a float.
            with_genres: (optional) Only include movies with the specified
                         genres.  Expected value is an integer (the id of
                         a genre).  Multiple values can be specified.
                         Comma separated indicates an 'AND' query, while
                         a pipe (|) separated value indicates an 'OR'.
            release_date.gte or release_date_gte: (optional) The minimum
                              release to include.  Expected format is
                              'YYYY-MM-DD'.
            releaste_date.lte or release_date_lte: (optional) The maximum
                              release to include.  Expected format is
                              'YYYY-MM-DD'.
            certification_country: (optional) Only include movies with
                                   certifications for a specific country. When
                                   this value is specified, 'certification.lte'
                                   is required. An ISO 3166-1 is expected.
            certification.lte or certification_lte: (optional) Only include
                               movies with this certification and lower.
                               Expected value is a valid certification for
                               the specified 'certification_country'.
            with_companies: (optional) Filter movies to include a specific
                            company.  Expected value is an integer (the id
                            of a company).  They can be comma separated
                            to indicate an 'AND' query.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        # Periods are not allowed in keyword arguments but several API
        # arguments contain periods. See both usages in tests/test_discover.py.
        for param in kwargs:
            if '_lte' in param:
                kwargs[param.replace('_lte', '.lte')] = kwargs.pop(param)
            if '_gte' in param:
                kwargs[param.replace('_gte', '.gte')] = kwargs.pop(param)

        path = self._get_path('movie')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def tv(self, **kwargs):
        """
        Discover TV shows by different types of data like average rating,
        number of votes, genres, the network they aired on and air dates.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            language: (optional) ISO 639-1 code.
            sort_by: (optional) Available options are 'vote_average.desc',
                     'vote_average.asc', 'first_air_date.desc',
                     'first_air_date.asc', 'popularity.desc', 'popularity.asc'
            first_air_year: (optional) Filter the results release dates to
                            matches that include this value. Expected value
                            is a year.
            vote_count.gte or vote_count_gte: (optional) Only include TV shows
                            that are equal to,
                            or have vote count higher than this value. Expected
                            value is an integer.
            vote_average.gte or vote_average_gte: (optional) Only include TV
                              shows that are equal
                              to, or have a higher average rating than this
                              value.  Expected value is a float.
            with_genres: (optional) Only include TV shows with the specified
                         genres. Expected value is an integer (the id of a
                         genre).  Multiple valued can be specified. Comma
                         separated indicates an 'AND' query, while a
                         pipe (|) separated value indicates an 'OR'.
            with_networks: (optional) Filter TV shows to include a specific
                           network. Expected value is an integer (the id of a
                           network).  They can be comma separated to indicate an
                           'AND' query.
            first_air_date.gte or first_air_date_gte: (optional) The minimum
                                release to include.
                                Expected format is 'YYYY-MM-DD'.
            first_air_date.lte or first_air_date_lte: (optional) The maximum
                                release to include.
                                Expected format is 'YYYY-MM-DD'.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        # Periods are not allowed in keyword arguments but several API
        # arguments contain periods. See both usages in tests/test_discover.py.
        for param in kwargs:
            if '_lte' in param:
                kwargs[param.replace('_lte', '.lte')] = kwargs.pop(param)
            if '_gte' in param:
                kwargs[param.replace('_gte', '.gte')] = kwargs.pop(param)

        path = self._get_path('tv')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response


# -*- coding: utf-8 -*-

"""
tmdbsimple.configuration
~~~~~~~~~~~~~~~~~~~~~~~~

This module implements the Configuration, Certifications, and Timezones
functionality of tmdbsimple.

Created by Celia Oakley on 2013-10-31.

:copyright: (c) 2013-2018 by Celia Oakley
:license: GPLv3, see LICENSE for more details
"""

from .base import TMDB

class Configuration(TMDB):
    """
    Configuration functionality.

    See: https://developers.themoviedb.org/3/configuration
    """
    BASE_PATH = 'configuration'
    URLS = {
        'info': '',
    }

    def info(self, **kwargs):
        """
        Get the system wide configuration info.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class Certifications(TMDB):
    """
    Certifications functionality.

    See: https://developers.themoviedb.org/3/certifications
    """
    BASE_PATH = 'certification'
    URLS = {
        'movie_list': '/movie/list',
    }

    def list(self, **kwargs):
        """
        Get the list of supported certifications for movies.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('movie_list')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class Timezones(TMDB):
    """
    Timezones functionality.

    See: https://developers.themoviedb.org/3/timezones
    """
    BASE_PATH = 'timezones'
    URLS = {
        'list': '/list',
    }

    def list(self, **kwargs):
        """
        Get the list of supported timezones for the API methods that support
        them.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('list')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response


# -*- coding: utf-8 -*-

"""
tmdbsimple.account
~~~~~~~~~~~~~~~~~~
This module implements the Account, Authentication, and Lists functionality
of tmdbsimple.

Created by Celia Oakley on 2013-10-31.

:copyright: (c) 2013-2018 by Celia Oakley
:license: GPLv3, see LICENSE for more details
"""

from .base import TMDB

class Account(TMDB):
    """
    Account functionality.

    See: https://developers.themoviedb.org/3/account
         https://www.themoviedb.org/documentation/api/sessions
    """
    BASE_PATH = 'account'
    URLS = {
        'info': '',
        'lists': '/{id}/lists',
        'favorite_movies': '/{id}/favorite/movies',
        'favorite_tv': '/{id}/favorite/tv',
        'favorite': '/{id}/favorite',
        'rated_movies': '/{id}/rated/movies',
        'rated_tv': '/{id}/rated/tv',
        'watchlist_movies': '/{id}/watchlist/movies',
        'watchlist_tv': '/{id}/watchlist/tv',
        'watchlist': '/{id}/watchlist',
    }

    def __init__(self, session_id):
        super(Account, self).__init__()
        self.session_id = session_id

    def info(self, **kwargs):
        """
        Get the basic information for an account.

        Call this method first, before calling other Account methods.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('info')
        kwargs.update({'session_id': self.session_id})

        response = self._GET(path, kwargs)
        self.id = response['id']
        self._set_attrs_to_values(response)
        return response

    def lists(self, **kwargs):
        """
        Get the lists that you have created and marked as a favorite.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('lists')
        kwargs.update({'session_id': self.session_id})

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def favorite_movies(self, **kwargs):
        """
        Get the list of favorite movies for an account.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            sort_by: (optional) 'created_at.asc' | 'created_at.desc'
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('favorite_movies')
        kwargs.update({'session_id': self.session_id})

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def favorite_tv(self, **kwargs):
        """
        Get the list of favorite TV series for an account.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            sort_by: (optional) 'created_at.asc' | 'created_at.desc'
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('favorite_tv')
        kwargs.update({'session_id': self.session_id})

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def favorite(self, **kwargs):
        """
        Add or remove a movie to an accounts favorite list.

        Args:
            media_type: 'movie' | 'tv'
            media_id: The id of the media.
            favorite: True (to add) | False (to remove).

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('favorite')
        kwargs.update({'session_id': self.session_id})

        payload = {
            'media_type': kwargs.pop('media_type', None),
            'media_id': kwargs.pop('media_id', None),
            'favorite': kwargs.pop('favorite', None),
        }

        response = self._POST(path, kwargs, payload)
        self._set_attrs_to_values(response)
        return response

    def rated_movies(self, **kwargs):
        """
        Get the list of rated movies (and associated rating) for an account.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            sort_by: (optional) 'created_at.asc' | 'created_at.desc'
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('rated_movies')
        kwargs.update({'session_id': self.session_id})

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def rated_tv(self, **kwargs):
        """
        Get the list of rated TV shows (and associated rating) for an account.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            sort_by: (optional) 'created_at.asc' | 'created_at.desc'
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('rated_tv')
        kwargs.update({'session_id': self.session_id})

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def watchlist_movies(self, **kwargs):
        """
        Get the list of movies on an account watchlist.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            sort_by: (optional) 'created_at.asc' | 'created_at.desc'
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('watchlist_movies')
        kwargs.update({'session_id': self.session_id})

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def watchlist_tv(self, **kwargs):
        """
        Get the list of TV series on an account watchlist.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            sort_by: (optional) 'created_at.asc' | 'created_at.desc'
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('watchlist_tv')
        kwargs.update({'session_id': self.session_id})

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def watchlist(self, **kwargs):
        """
        Add or remove a movie to an accounts watch list.

        Args:
            media_type: 'movie' | 'tv'
            media_id: The id of the media.
            watchlist: True (to add) | False (to remove).

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('watchlist')
        kwargs.update({'session_id': self.session_id})

        payload = {
            'media_type': kwargs.pop('media_type', None),
            'media_id': kwargs.pop('media_id', None),
            'watchlist': kwargs.pop('watchlist', None),
        }

        response = self._POST(path, kwargs, payload)
        self._set_attrs_to_values(response)
        return response

class Authentication(TMDB):
    """
    Authentication functionality.

    See: https://developers.themoviedb.org/3/authentication
         https://www.themoviedb.org/documentation/api/sessions
    """
    BASE_PATH = 'authentication'
    URLS = {
        'token_new': '/token/new',
        'token_validate_with_login': '/token/validate_with_login',
        'session_new': '/session/new',
        'guest_session_new': '/guest_session/new',
    }

    def token_new(self, **kwargs):
        """
        Generate a valid request token for user based authentication.

        A request token is required to ask the user for permission to
        access their account.

        After obtaining the request_token, either:
        (1) Direct your user to:
                https://www.themoviedb.org/authenticate/REQUEST_TOKEN
        or:
        (2) Call token_validate_with_login() below.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('token_new')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def token_validate_with_login(self, **kwargs):
        """
        Authenticate a user with a TMDb username and password.  The user
        must have a verified email address and be registered on TMDb.

        Args:
            request_token: The token you generated for the user to approve.
            username: The user's username on TMDb.
            password: The user's password on TMDb.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('token_validate_with_login')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def session_new(self, **kwargs):
        """
        Generate a session id for user based authentication.

        A session id is required in order to use any of the write methods.

        Args:
            request_token: The token you generated for the user to approve.
                           The token needs to be approved before being
                           used here.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('session_new')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def guest_session_new(self, **kwargs):
        """
        Generate a guest session id.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('guest_session_new')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class GuestSessions(TMDB):
    """
    Guest Sessions functionality.

    See: https://developers.themoviedb.org/3/guest-sessions
    """
    BASE_PATH = 'guest_session'
    URLS = {
        'rated_movies': '/{guest_session_id}/rated_movies',
    }

    def __init__(self, guest_session_id=0):
        super(GuestSessions, self).__init__()
        self.guest_session_id = guest_session_id

    def rated_movies(self, **kwargs):
        """
        Get a list of rated moview for a specific guest session id.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            sort_by: (optional) 'created_at.asc' | 'created_at.desc'
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_guest_session_id_path('rated_movies')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class Lists(TMDB):
    """
    Lists functionality.

    See: https://developers.themoviedb.org/3/lists
    """
    BASE_PATH = 'list'
    URLS = {
        'info': '/{id}',
        'item_status': '/{id}/item_status',
        'create_list': '',
        'add_item': '/{id}/add_item',
        'remove_item': '/{id}/remove_item',
        'clear': '/{id}/clear',
    }

    def __init__(self, id=0, session_id=0):
        super(Lists, self).__init__()
        self.id = id
        self.session_id = session_id

    def info(self, **kwargs):
        """
        Get a list by id.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def item_status(self, **kwargs):
        """
        Check to see if a movie id is already added to a list.

        Args:
            movie_id: The id of the movie.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('item_status')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def create_list(self, **kwargs):
        """
        Create a new list.

        A valid session id is required.

        Args:
            name: Name of the list.
            description: Description of the list.
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('create_list')
        kwargs.update({'session_id': self.session_id})

        payload = {
            'name': kwargs.pop('name', None),
            'description': kwargs.pop('description', None),
        }
        if 'language' in kwargs:
            payload['language'] = kwargs['language']

        response = self._POST(path, kwargs, payload)
        self._set_attrs_to_values(response)
        return response

    def add_item(self, **kwargs):
        """
        Add new movies to a list that the user created.

        A valid session id is required.

        Args:
            media_id: A movie id.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('add_item')
        kwargs.update({'session_id': self.session_id})

        payload = {
            'media_id': kwargs.pop('media_id', None),
        }

        response = self._POST(path, kwargs, payload)
        self._set_attrs_to_values(response)
        return response

    def remove_item(self, **kwargs):
        """
        Delete movies from a list that the user created.

        A valid session id is required.

        Args:
            media_id: A movie id.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('remove_item')
        kwargs.update({'session_id': self.session_id})

        payload = {
            'media_id': kwargs.pop('media_id', None),
        }

        response = self._POST(path, kwargs, payload)
        self._set_attrs_to_values(response)
        return response

    def clear_list(self, **kwargs):
        """
        Clears all of the items within a list. This is an irreversible action
        and should be treated with caution.

        A valid session id is required.

        Args:
            confirm: True (do it) | False (don't do it)

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('clear')
        kwargs.update({'session_id': self.session_id})

        payload = {}

        response = self._POST(path, kwargs, payload)
        self._set_attrs_to_values(response)
        return response


# -*- coding: utf-8 -*-

"""
tmdbsimple.tv
~~~~~~~~~~~~~
This module implements the TV, TV Seasons, TV Episodes, and Networks
functionality of tmdbsimple.

Created by Celia Oakley on 2013-10-31.

:copyright: (c) 2013-2018 by Celia Oakley
:license: GPLv3, see LICENSE for more details
"""

from .base import TMDB

class TV(TMDB):
    """
    TV functionality.

    See: https://developers.themoviedb.org/3/tv
    """
    BASE_PATH = 'tv'
    URLS = {
        'info': '/{id}',
        'alternative_titles': '/{id}/alternative_titles',
        'content_ratings': '/{id}/content_ratings',
        'credits': '/{id}/credits',
        'external_ids': '/{id}/external_ids',
        'images': '/{id}/images',
        'rating': '/{id}/rating',
        'similar': '/{id}/similar',
        'recommendations': '/{id}/recommendations',
        'translations': '/{id}/translations',
        'videos': '/{id}/videos',
        'latest': '/latest',
        'on_the_air': '/on_the_air',
        'airing_today': '/airing_today',
        'top_rated': '/top_rated',
        'popular': '/popular',
    }

    def __init__(self, id=0):
        super(TV, self).__init__()
        self.id = id

    def info(self, **kwargs):
        """
        Get the primary information about a TV series by id.

        Args:
            language: (optional) ISO 639 code.
            append_to_response: (optional) Comma separated, any TV series
                                method.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def alternative_titles(self, **kwargs):
        """
        Get the alternative titles for a specific tv id.

        Args:
            language: (optional) ISO 3166-1 code.
            append_to_response: (optional) Comma separated, any tv method.
        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('alternative_titles')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def content_ratings(self, **kwargs):
        """
        Get the content ratings for a TV Series.

        Args:
            language: (optional) ISO 639 code.
            append_to_response: (optional) Comma separated, any collection
                                method.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('content_ratings')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def credits(self, **kwargs):
        """
        Get the cast & crew information about a TV series. Just like the
        website, we pull this information from the last season of the series.

        Args:
            language: (optional) ISO 639 code.
            append_to_response: (optional) Comma separated, any collection
                                method.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('credits')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def external_ids(self, **kwargs):
        """
        Get the external ids that we have stored for a TV series.

        Args:
            language: (optional) ISO 639 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('external_ids')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def images(self, **kwargs):
        """
        Get the images (posters and backdrops) for a TV series.

        Args:
            language: (optional) ISO 639 code.
            include_image_language: (optional) Comma separated, a valid
                                    ISO 69-1.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('images')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def rating(self, **kwargs):
        """
        This method lets users rate a TV show. A valid session id or guest
        session id is required.

        Args:
            session_id: see Authentication.
            guest_session_id: see Authentication.
            value: Rating value.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('rating')

        payload = {
            'value': kwargs.pop('value', None),
        }

        response = self._POST(path, kwargs, payload)
        self._set_attrs_to_values(response)
        return response

    def similar(self, **kwargs):
        """
        Get the similar TV series for a specific TV series id.

        Args:
            page: (optional) Minimum value of 1.  Expected value is an integer.
            language: (optional) ISO 639-1 code.
            append_to_response: (optional) Comma separated, any TV method.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('similar')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def recommendations(self, **kwargs):
        """
        Get the recommendations for TV series for a specific TV series id.

        Args:
            page: (optional) Minimum value of 1.  Expected value is an integer.
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('recommendations')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def translations(self, **kwargs):
        """
        Get the list of translations that exist for a TV series. These
        translations cascade down to the episode level.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('translations')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def videos(self, **kwargs):
        """
        Get the videos that have been added to a TV series (trailers, opening
        credits, etc...).

        Args:
            language: (optional) ISO 639 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('videos')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def latest(self, **kwargs):
        """
        Get the most newly created TV show. This is a live response
		and will continuously change.

        Args:
            language: (optional) ISO 639 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('latest')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def on_the_air(self, **kwargs):
        """
        Get the list of TV shows that are currently on the air. This query
        looks for any TV show that has an episode with an air date in the
        next 7 days.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            language: (optional) ISO 639 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('on_the_air')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def airing_today(self, **kwargs):
        """
        Get the list of TV shows that air today. Without a specified timezone,
        this query defaults to EST (Eastern Time UTC-05:00).

        Args:
            page: (optional) Minimum 1, maximum 1000.
            language: (optional) ISO 639 code.
            timezone: (optional) Valid value from the list of timezones.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('airing_today')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def top_rated(self, **kwargs):
        """
        Get the list of top rated TV shows. By default, this list will only
        include TV shows that have 2 or more votes. This list refreshes every
        day.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            language: (optional) ISO 639 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('top_rated')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def popular(self, **kwargs):
        """
        Get the list of popular TV shows. This list refreshes every day.

        Args:
            page: (optional) Minimum 1, maximum 1000.
            language: (optional) ISO 639 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('popular')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class TV_Seasons(TMDB):
    """
    TV Seasons functionality.

    See: https://developers.themoviedb.org/3/tv-seasons
    """
    BASE_PATH = 'tv/{series_id}/season/{season_number}'
    URLS = {
        'info': '',
        'credits': '/credits',
        'external_ids': '/external_ids',
        'images': '/images',
        'videos': '/videos',
    }

    def __init__(self, series_id, season_number):
        super(TV_Seasons, self).__init__()
        self.series_id = series_id
        self.season_number = season_number

    def info(self, **kwargs):
        """
        Get the primary information about a TV season by its season number.

        Args:
            language: (optional) ISO 639 code.
            append_to_response: (optional) Comma separated, any TV series
                                method.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_series_id_season_number_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def credits(self, **kwargs):
        """
        Get the cast & crew credits for a TV season by season number.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_series_id_season_number_path('credits')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def external_ids(self, **kwargs):
        """
        Get the external ids that we have stored for a TV season by season
        number.

        Args:
            language: (optional) ISO 639 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_series_id_season_number_path('external_ids')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def images(self, **kwargs):
        """
        Get the images (posters) that we have stored for a TV season by season
        number.

        Args:
            language: (optional) ISO 639 code.
            include_image_language: (optional) Comma separated, a valid
                                    ISO 69-1.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_series_id_season_number_path('images')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def videos(self, **kwargs):
        """
        Get the videos that have been added to a TV season (trailers, teasers,
        etc...).

        Args:
            language: (optional) ISO 639 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_series_id_season_number_path('videos')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class TV_Episodes(TMDB):
    """
    TV Episodes functionality.

    See: https://developers.themoviedb.org/3/tv-episodes
    """
    BASE_PATH = 'tv/{series_id}/season/{season_number}/episode/{episode_number}'
    URLS = {
        'info': '',
        'credits': '/credits',
        'external_ids': '/external_ids',
        'images': '/images',
        'rating': '/rating',
        'videos': '/videos',
    }

    def __init__(self, series_id, season_number, episode_number):
        super(TV_Episodes, self).__init__()
        self.series_id = series_id
        self.season_number = season_number
        self.episode_number = episode_number

    def info(self, **kwargs):
        """
        Get the primary information about a TV episode by combination of a
        season and episode number.

        Args:
            language: (optional) ISO 639 code.
            append_to_response: (optional) Comma separated, any TV series
                                method.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_series_id_season_number_episode_number_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def credits(self, **kwargs):
        """
        Get the TV episode credits by combination of season and episode number.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_series_id_season_number_episode_number_path('credits')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def external_ids(self, **kwargs):
        """
        Get the external ids for a TV episode by combination of a season and
        episode number.

        Args:
            language: (optional) ISO 639 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_series_id_season_number_episode_number_path(
            'external_ids')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def images(self, **kwargs):
        """
        Get the images (episode stills) for a TV episode by combination of a
        season and episode number. Since episode stills don't have a language,
        this call will always return all images.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_series_id_season_number_episode_number_path('images')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def rating(self, **kwargs):
        """
        This method lets users rate a TV episode. A valid session id or guest
        session id is required.

        Args:
            session_id: see Authentication.
            guest_session_id: see Authentication.
            value: Rating value.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_series_id_season_number_episode_number_path('rating')

        payload = {
            'value': kwargs.pop('value', None),
        }

        response = self._POST(path, kwargs, payload)
        self._set_attrs_to_values(response)
        return response

    def videos(self, **kwargs):
        """
        Get the videos that have been added to a TV episode (teasers, clips,
        etc...).

        Args:
            language: (optional) ISO 639 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_series_id_season_number_episode_number_path('videos')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

class Networks(TMDB):
    """
    Networks functionality.

    See: https://developers.themoviedb.org/3/networks
    """
    BASE_PATH = 'network'
    URLS = {
        'info': '/{id}',
    }

    def __init__(self, id):
        super(Networks, self).__init__()
        self.id = id

    def info(self, **kwargs):
        """
        This method is used to retrieve the basic information about a TV
        network. You can use this ID to search for TV shows with the discover.
        At this time we don't have much but this will be fleshed out over time.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_id_path('info')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response


# -*- coding: utf-8 -*-

"""
tmdbsimple.base
~~~~~~~~~~~~~~~
This module implements the base class of tmdbsimple.

Created by Celia Oakley on 2013-10-31.

:copyright: (c) 2013-2018 by Celia Oakley
:license: GPLv3, see LICENSE for more details
"""

import json
import requests

class APIKeyError(Exception):
    pass

class TMDB(object):
    headers = {'Content-Type': 'application/json',
               'Accept': 'application/json',
               'Connection': 'close'}
    BASE_PATH = ''
    URLS = {}

    def __init__(self):
        from . import API_VERSION
        self.base_uri = 'https://api.themoviedb.org'
        self.base_uri += '/{version}'.format(version=API_VERSION)

    def _get_path(self, key):
        return self.BASE_PATH + self.URLS[key]

    def _get_id_path(self, key):
        return self._get_path(key).format(id=self.id)

    def _get_guest_session_id_path(self, key):
        return self._get_path(key).format(
            guest_session_id=self.guest_session_id)

    def _get_credit_id_path(self, key):
        return self._get_path(key).format(credit_id=self.credit_id)

    def _get_series_id_season_number_path(self, key):
        return self._get_path(key).format(series_id=self.series_id,
            season_number=self.season_number)

    def _get_series_id_season_number_episode_number_path(self, key):
        return self._get_path(key).format(series_id=self.series_id,
            season_number=self.season_number,
            episode_number=self.episode_number)

    def _get_complete_url(self, path):
        return '{base_uri}/{path}'.format(base_uri=self.base_uri, path=path)

    def _get_params(self, params):
        from . import API_KEY
        if not API_KEY:
            raise APIKeyError

        api_dict = {'api_key': API_KEY}
        if params:
            params.update(api_dict)
        else:
            params = api_dict
        return params

    def _request(self, method, path, params=None, payload=None):
        url = self._get_complete_url(path)
        params = self._get_params(params)

        response = requests.request(
            method, url, params=params,
            data=json.dumps(payload) if payload else payload,
            headers=self.headers)

        response.raise_for_status()
        response.encoding = 'utf-8'
        return response.json()

    def _GET(self, path, params=None):
        return self._request('GET', path, params=params)

    def _POST(self, path, params=None, payload=None):
        return self._request('POST', path, params=params, payload=payload)

    def _DELETE(self, path, params=None, payload=None):
        return self._request('DELETE', path, params=params, payload=payload)

    def _set_attrs_to_values(self, response={}):
        """
        Set attributes to dictionary values.

        - e.g.
        >>> import tmdbsimple as tmdb
        >>> movie = tmdb.Movies(103332)
        >>> response = movie.info()
        >>> movie.title  # instead of response['title']
        """
        if isinstance(response, dict):
            for key in response.keys():
                if not hasattr(self, key) or not callable(getattr(self, key)):
                    setattr(self, key, response[key])


# -*- coding: utf-8 -*-

"""
tmdbsimple.search
~~~~~~~~~~~~~~~~~
This module implements the Search functionality of tmdbsimple.

Created by Celia Oakley on 2013-10-31.

:copyright: (c) 2013-2018 by Celia Oakley
:license: GPLv3, see LICENSE for more details
"""

from .base import TMDB

class Search(TMDB):
    """
    Search functionality

    See: https://developers.themoviedb.org/3/search
    """
    BASE_PATH = 'search'
    URLS = {
        'movie': '/movie',
        'collection': '/collection',
        'tv': '/tv',
        'person': '/person',
        'company': '/company',
        'keyword': '/keyword',
        'multi': '/multi'
    }

    def movie(self, **kwargs):
        """
        Search for movies by title.

        Args:
            query: CGI escpaed string.
            page: (optional) Minimum value of 1. Expected value is an integer.
            language: (optional) ISO 639-1 code.
            include_adult: (optional) Toggle the inclusion of adult titles.
                           Expected value is True or False.
            year: (optional) Filter the results release dates to matches that
                  include this value.
            primary_release_year: (optional) Filter the results so that only
                                  the primary release dates have this value.
            search_type: (optional) By default, the search type is 'phrase'.
                         This is almost guaranteed the option you will want.
                         It's a great all purpose search type and by far the
                         most tuned for every day querying. For those wanting
                         more of an "autocomplete" type search, set this
                         option to 'ngram'.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('movie')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def collection(self, **kwargs):
        """
        Search for collections by name.

        Args:
            query: CGI escpaed string.
            page: (optional) Minimum value of 1. Expected value is an integer.
            language: (optional) ISO 639-1 code.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('collection')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def tv(self, **kwargs):
        """
        Search for TV shows by title.

        Args:
            query: CGI escpaed string.
            page: (optional) Minimum value of 1. Expected value is an integer.
            language: (optional) ISO 639-1 code.
            first_air_date_year: (optional) Filter the results to only match
                                 shows that have a air date with with value.
            search_type: (optional) By default, the search type is 'phrase'.
                         This is almost guaranteed the option you will want.
                         It's a great all purpose search type and by far the
                         most tuned for every day querying. For those wanting
                         more of an "autocomplete" type search, set this
                         option to 'ngram'.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('tv')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def person(self, **kwargs):
        """
        Search for people by name.

        Args:
            query: CGI escpaed string.
            page: (optional) Minimum value of 1. Expected value is an integer.
            include_adult: (optional) Toggle the inclusion of adult titles.
                           Expected value is True or False.
            search_type: (optional) By default, the search type is 'phrase'.
                         This is almost guaranteed the option you will want.
                         It's a great all purpose search type and by far the
                         most tuned for every day querying. For those wanting
                         more of an "autocomplete" type search, set this
                         option to 'ngram'.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('person')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def company(self, **kwargs):
        """
        Search for companies by name.

        Args:
            query: CGI escpaed string.
            page: (optional) Minimum value of 1. Expected value is an integer.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('company')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def keyword(self, **kwargs):
        """
        Search for keywords by name.

        Args:
            query: CGI escpaed string.
            page: (optional) Minimum value of 1. Expected value is an integer.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('keyword')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response

    def multi(self, **kwargs):
        """
        Search the movie, tv show and person collections with a single query.

        Args:
            query: CGI escpaed string.
            page: (optional) Minimum value of 1. Expected value is an integer.
            language: (optional) ISO 639-1 code.
            include_adult: (optional) Toggle the inclusion of adult titles.
                           Expected value is True or False.

        Returns:
            A dict respresentation of the JSON returned from the API.
        """
        path = self._get_path('multi')

        response = self._GET(path, kwargs)
        self._set_attrs_to_values(response)
        return response


#!/usr/bin/python
# -*- coding: utf-8 -*-
#File originally part of moses package: http://www.statmt.org/moses/ (as bleu.py)
#Stripped of unused code to reduce number of libraries used

# $Id$

'''Provides:

cook_refs(refs, n=4): Transform a list of reference sentences as strings into a form usable by cook_test().
cook_test(test, refs, n=4): Transform a test sentence as a string (together with the cooked reference sentences) into a form usable by score_cooked().
score_cooked(alltest, n=4): Score a list of cooked test sentences.

score_set(s, testid, refids, n=4): Interface with dataset.py; calculate BLEU score of testid against refids.

The reason for breaking the BLEU computation into three phases cook_refs(), cook_test(), and score_cooked() is to allow the caller to calculate BLEU scores for multiple test sets as efficiently as possible.
'''

from __future__ import division, print_function
import sys, math, re, xml.sax.saxutils

# Added to bypass NIST-style pre-processing of hyp and ref files -- wade
nonorm = 0

preserve_case = False
eff_ref_len = "shortest"

normalize1 = [
    ('<skipped>', ''),         # strip "skipped" tags
    (r'-\n', ''),              # strip end-of-line hyphenation and join lines
    (r'\n', ' '),              # join lines
#    (r'(\d)\s+(?=\d)', r'\1'), # join digits
]
normalize1 = [(re.compile(pattern), replace) for (pattern, replace) in normalize1]

normalize2 = [
    (r'([\{-\~\[-\` -\&\(-\+\:-\@\/])',r' \1 '), # tokenize punctuation. apostrophe is missing
    (r'([^0-9])([\.,])',r'\1 \2 '),              # tokenize period and comma unless preceded by a digit
    (r'([\.,])([^0-9])',r' \1 \2'),              # tokenize period and comma unless followed by a digit
    (r'([0-9])(-)',r'\1 \2 ')                    # tokenize dash when preceded by a digit
]
normalize2 = [(re.compile(pattern), replace) for (pattern, replace) in normalize2]

#combine normalize2 into a single regex.
normalize3 = re.compile(r'([\{-\~\[-\` -\&\(-\+\:-\@\/])|(?:(?<![0-9])([\.,]))|(?:([\.,])(?![0-9]))|(?:(?<=[0-9])(-))')

def normalize(s):
    '''Normalize and tokenize text. This is lifted from NIST mteval-v11a.pl.'''
    # Added to bypass NIST-style pre-processing of hyp and ref files -- wade
    if (nonorm):
        return s.split()
    try:
        s.split()
    except:
        s = " ".join(s)
    # language-independent part:
    for (pattern, replace) in normalize1:
        s = re.sub(pattern, replace, s)
    s = xml.sax.saxutils.unescape(s, {'"':'"'})
    # language-dependent part (assuming Western languages):
    s = " %s " % s
    if not preserve_case:
        s = s.lower()         # this might not be identical to the original
    return [tok for tok in normalize3.split(s) if tok and tok != ' ']

def count_ngrams(words, n=4):
    counts = {}
    for k in range(1,n+1):
        for i in range(len(words)-k+1):
            ngram = tuple(words[i:i+k])
            counts[ngram] = counts.get(ngram, 0)+1
    return counts

def cook_refs(refs, n=4):
    '''Takes a list of reference sentences for a single segment
    and returns an object that encapsulates everything that BLEU
    needs to know about them.'''

    refs = [normalize(ref) for ref in refs]
    maxcounts = {}
    for ref in refs:
        counts = count_ngrams(ref, n)
        for (ngram,count) in list(counts.items()):
            maxcounts[ngram] = max(maxcounts.get(ngram,0), count)
    return ([len(ref) for ref in refs], maxcounts)

def cook_ref_set(ref, n=4):
    '''Takes a reference sentences for a single segment
    and returns an object that encapsulates everything that BLEU
    needs to know about them.  Also provides a set cause bleualign wants it'''
    ref = normalize(ref)
    counts = count_ngrams(ref, n)
    return (len(ref), counts, frozenset(counts))

def cook_test(test, args, n=4):
    '''Takes a test sentence and returns an object that
    encapsulates everything that BLEU needs to know about it.'''

    reflens, refmaxcounts = args
    test = normalize(test)
    result = {}
    result["testlen"] = len(test)

    # Calculate effective reference sentence length.

    if eff_ref_len == "shortest":
        result["reflen"] = min(reflens)
    elif eff_ref_len == "average":
        result["reflen"] = float(sum(reflens))/len(reflens)
    elif eff_ref_len == "closest":
        min_diff = None
        for reflen in reflens:
            if min_diff is None or abs(reflen-len(test)) < min_diff:
                min_diff = abs(reflen-len(test))
                result['reflen'] = reflen

    result["guess"] = [max(len(test)-k+1,0) for k in range(1,n+1)]

    result['correct'] = [0]*n
    counts = count_ngrams(test, n)
    for (ngram, count) in list(counts.items()):
        result["correct"][len(ngram)-1] += min(refmaxcounts.get(ngram,0), count)

    return result

def score_cooked(allcomps, n=4):
    totalcomps = {'testlen':0, 'reflen':0, 'guess':[0]*n, 'correct':[0]*n}
    for comps in allcomps:
        for key in ['testlen','reflen']:
            totalcomps[key] += comps[key]
        for key in ['guess','correct']:
            for k in range(n):
                totalcomps[key][k] += comps[key][k]
    logbleu = 0.0
    for k in range(n):
        if totalcomps['correct'][k] == 0:
            return 0.0
        #log.write("%d-grams: %f\n" % (k,float(totalcomps['correct'][k])/totalcomps['guess'][k]))
        logbleu += math.log(totalcomps['correct'][k])-math.log(totalcomps['guess'][k])
    logbleu /= float(n)
    #log.write("Effective reference length: %d test length: %d\n" % (totalcomps['reflen'], totalcomps['testlen']))
    logbleu += min(0,1-float(totalcomps['reflen'])/totalcomps['testlen'])
    return math.exp(logbleu)


# -*- coding: utf-8 -*-

import math

# Based on Gale & Church 1993,
# "A Program for Aligning Sentences in Bilingual Corpora"

infinity = float("inf")

def erfcc(x):
    """Complementary error function."""
    z = abs(x)
    t = 1 / (1 + 0.5 * z)
    r = t * math.exp(-z * z -
                     1.26551223 + t *
                     (1.00002368 + t *
                      (.37409196 + t *
                       (.09678418 + t *
                        (-.18628806 + t *
                         (.27886807 + t *
                          (-1.13520398 + t *
                           (1.48851587 + t *
                            (-.82215223 + t * .17087277)))))))))
    if (x >= 0.):
        return r
    else:
        return 2. - r

def norm_cdf(x):
    """Return the area under the normal distribution from M{-∞..x}."""
    return 1 - 0.5 * erfcc(x / math.sqrt(2))

class LanguageIndependent(object):
    # These are the language-independent probabilities and parameters
    # given in Gale & Church

    # for the computation, l_1 is always the language with less characters
    PRIORS = {
        (1, 0): 0.0099,
        (0, 1): 0.0099,
        (1, 1): 0.89,
        (2, 1): 0.089,
        (1, 2): 0.089,
        (2, 2): 0.011,
    }

    AVERAGE_CHARACTERS = 1
    VARIANCE_CHARACTERS = 6.8

def trace(backlinks, source, target):
    links = set()
    pos = (len(source) - 1, len(target) - 1)

    #while pos != (-1, -1):
    while pos[0] != -1 and pos[1] != -1:
        #print(pos)
        #print(backlinks)
        #print(backlinks[pos])
        s, t = backlinks[pos]
        for i in range(s):
            for j in range(t):
                links.add((pos[0] - i, pos[1] - j))
        pos = (pos[0] - s, pos[1] - t)

    return links

def align_probability(i, j, source_sentences, target_sentences, alignment, params):
    """Returns the probability of the two sentences C{source_sentences[i]}, C{target_sentences[j]}
    being aligned with a specific C{alignment}.

    @param i: The offset of the source sentence.
    @param j: The offset of the target sentence.
    @param source_sentences: The list of source sentence lengths.
    @param target_sentences: The list of target sentence lengths.
    @param alignment: The alignment type, a tuple of two integers.
    @param params: The sentence alignment parameters.

    @returns: The probability of a specific alignment between the two sentences, given the parameters.
    """
    l_s = sum(source_sentences[i - offset] for offset in range(alignment[0]))
    l_t = sum(target_sentences[j - offset] for offset in range(alignment[1]))
    try:
        # actually, the paper says l_s * params.VARIANCE_CHARACTERS, this is based on the C
        # reference implementation. With l_s in the denominator, insertions are impossible.
        m = (l_s + l_t / params.AVERAGE_CHARACTERS) / 2
        delta = (l_t - l_s * params.AVERAGE_CHARACTERS) / math.sqrt(m * params.VARIANCE_CHARACTERS)
    except ZeroDivisionError:
        delta = infinity

    return 2 * (1 - norm_cdf(abs(delta))) * params.PRIORS[alignment]

def align_blocks(source_sentences, target_sentences, params = LanguageIndependent):
    """Creates the sentence alignment of two blocks of texts (usually paragraphs).

    @param source_sentences: The list of source sentence lengths.
    @param target_sentences: The list of target sentence lengths.
    @param params: the sentence alignment parameters.

    @return: The sentence alignments, a list of index pairs.
    """
    alignment_types = list(params.PRIORS.keys())

    # there are always three rows in the history (with the last of them being filled)
    # and the rows are always |target_text| + 2, so that we never have to do
    # boundary checks
    D = [(len(target_sentences) + 2) * [0] for x in range(2)]

    # for the first sentence, only substitution, insertion or deletion are
    # allowed, and they are all equally likely ( == 1)

    D.append([0, 1])
    try:
      D[-2][1] = 1
      D[-2][2] = 1
    except:
      pass

    backlinks = {}

    for i in range(len(source_sentences)):
        for j in range(len(target_sentences)):
            m = []
            for a in alignment_types:
                k = D[-(1 + a[0])][j + 2 - a[1]]
                if k > 0:
                    p = k * \
                      align_probability(i, j, source_sentences, target_sentences, a, params)
                    m.append((p, a))

            if len(m) > 0:
                v = max(m)
                backlinks[(i, j)] = v[1]
                D[-1].append(v[0])
            else:
                backlinks[(i, j)] = (1, 1)
                D[-1].append(0)

        D.pop(0)
        D.append([0, 0])

    return trace(backlinks, source_sentences, target_sentences)

def align_texts(source_blocks, target_blocks, params = LanguageIndependent):
    """Creates the sentence alignment of two texts.

    Texts can consist of several blocks. Block boundaries cannot be crossed by sentence
    alignment links.

    Each block consists of a list that contains the lengths (in characters) of the sentences
    in this block.

    @param source_blocks: The list of blocks in the source text.
    @param target_blocks: The list of blocks in the target text.
    @param params: the sentence alignment parameters.

    @returns: A list of sentence alignment lists
    """
    if len(source_blocks) != len(target_blocks):
        raise ValueError("Source and target texts do not have the same number of blocks.")

    return [align_blocks(source_block, target_block, params)
            for source_block, target_block in zip(source_blocks, target_blocks)]

def split_at(it, split_value):
    """Splits an iterator C{it} at values of C{split_value}.

    Each instance of C{split_value} is swallowed. The iterator produces
    subiterators which need to be consumed fully before the next subiterator
    can be used.
    """
    def _chunk_iterator(first):
        v = first
        while v != split_value:
            yield v
            v = next(it)

    while True:
        yield _chunk_iterator(next(it))

def parse_token_stream(stream, soft_delimiter, hard_delimiter):
    """Parses a stream of tokens and splits it into sentences (using C{soft_delimiter} tokens)
    and blocks (using C{hard_delimiter} tokens) for use with the L{align_texts} function.
    """
    return [
        [sum(len(token) for token in sentence_it)
         for sentence_it in split_at(block_it, soft_delimiter)]
        for block_it in split_at(stream, hard_delimiter)]

if __name__ == "__main__":
    import sys
    from contextlib import nested

    with nested(open(sys.argv[1], "r"), open(sys.argv[2], "r")) as (s, t):
        source = parse_token_stream((l.strip() for l in s), ".EOS", ".EOP")
        target = parse_token_stream((l.strip() for l in t), ".EOS", ".EOP")
        print((align_texts(source, target)))