mirror of
https://github.com/ets-labs/python-dependency-injector.git
synced 2025-05-24 15:29:06 +03:00
Rework movie lister example app
This commit is contained in:
parent
ca18fea26c
commit
a21c0c6160
67
examples/miniapps/movie-lister/README.rst
Normal file
67
examples/miniapps/movie-lister/README.rst
Normal file
|
@ -0,0 +1,67 @@
|
|||
Movie lister - a naive example of dependency injection in Python
|
||||
================================================================
|
||||
|
||||
This is a Python implementation of the dependency injection example from Martin Fowler's
|
||||
article about dependency injection and inversion of control:
|
||||
|
||||
http://www.martinfowler.com/articles/injection.html
|
||||
|
||||
Create virtual environment:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
virtualenv venv
|
||||
. venv/bin/activate
|
||||
|
||||
Install requirements:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pip install -r requirements.txt
|
||||
|
||||
To run the application do:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
MOVIE_STORAGE_TYPE=csv python -m movies
|
||||
MOVIE_STORAGE_TYPE=sqlite python -m movies
|
||||
|
||||
The output should be something like:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
[Movie(name='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence')]
|
||||
[Movie(name='The 33', year=2015, director='Patricia Riggen')]
|
||||
[Movie(name='Star Wars: Episode VII - The Force Awakens', year=2015, director='JJ Abrams')]
|
||||
[Movie(name='The Hunger Games: Mockingjay - Part 2', year=2015, director='Francis Lawrence'), Movie(name='The 33', year=2015, director='Patricia Riggen'), Movie(name='Star Wars: Episode VII - The Force Awakens', year=2015, director='JJ Abrams')]
|
||||
|
||||
To run the tests do:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
pytest movies/tests.py --cov=movies
|
||||
|
||||
The output should be something like:
|
||||
|
||||
.. code-block::
|
||||
|
||||
platform darwin -- Python 3.8.3, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
|
||||
plugins: cov-2.10.0
|
||||
collected 2 items
|
||||
|
||||
movies/tests.py .. [100%]
|
||||
|
||||
---------- coverage: platform darwin, python 3.8.3-final-0 -----------
|
||||
Name Stmts Miss Cover
|
||||
------------------------------------------
|
||||
movies/__init__.py 0 0 100%
|
||||
movies/__main__.py 15 15 0%
|
||||
movies/containers.py 9 0 100%
|
||||
movies/finders.py 9 0 100%
|
||||
movies/fixtures.py 1 0 100%
|
||||
movies/listers.py 8 0 100%
|
||||
movies/models.py 7 1 86%
|
||||
movies/storages.py 32 17 47%
|
||||
movies/tests.py 24 0 100%
|
||||
------------------------------------------
|
||||
TOTAL 105 33 69%
|
8
examples/miniapps/movie-lister/config.yml
Normal file
8
examples/miniapps/movie-lister/config.yml
Normal file
|
@ -0,0 +1,8 @@
|
|||
storage:
|
||||
|
||||
csv:
|
||||
path: "data/movies.csv"
|
||||
delimiter: ","
|
||||
|
||||
sqlite:
|
||||
path: "data/movies.db"
|
1
examples/miniapps/movie-lister/movies/__init__.py
Normal file
1
examples/miniapps/movie-lister/movies/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
"""Top-level package."""
|
23
examples/miniapps/movie-lister/movies/__main__.py
Normal file
23
examples/miniapps/movie-lister/movies/__main__.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
"""Main module."""
|
||||
|
||||
from .containers import ApplicationContainer
|
||||
|
||||
|
||||
def main():
|
||||
container = ApplicationContainer()
|
||||
container.config.from_yaml('config.yml')
|
||||
container.config.storage.type.from_env('MOVIE_STORAGE_TYPE')
|
||||
|
||||
storage = container.storage()
|
||||
fixtures = container.fixtures()
|
||||
storage.load_all(fixtures)
|
||||
|
||||
lister = container.lister()
|
||||
print(lister.movies_directed_by('Francis Lawrence'))
|
||||
print(lister.movies_directed_by('Patricia Riggen'))
|
||||
print(lister.movies_directed_by('JJ Abrams'))
|
||||
print(lister.movies_released_in(2015))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
39
examples/miniapps/movie-lister/movies/containers.py
Normal file
39
examples/miniapps/movie-lister/movies/containers.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
"""Containers module."""
|
||||
|
||||
from dependency_injector import containers, providers
|
||||
|
||||
from . import finders, listers, storages, models, fixtures
|
||||
|
||||
|
||||
class ApplicationContainer(containers.DeclarativeContainer):
|
||||
|
||||
config = providers.Configuration()
|
||||
|
||||
fixtures = providers.Object(fixtures.MOVIES_SAMPLE_DATA)
|
||||
|
||||
storage = providers.Singleton(
|
||||
providers.Selector(
|
||||
config.storage.type,
|
||||
csv=providers.Factory(
|
||||
storages.CsvMovieStorage,
|
||||
options=config.storage[config.storage.type],
|
||||
),
|
||||
sqlite=providers.Factory(
|
||||
storages.SqliteMovieStorage,
|
||||
options=config.storage[config.storage.type],
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
movie = providers.Factory(models.Movie)
|
||||
|
||||
finder = providers.Factory(
|
||||
finders.MovieFinder,
|
||||
movie_factory=movie.provider,
|
||||
movie_storage=storage,
|
||||
)
|
||||
|
||||
lister = providers.Factory(
|
||||
listers.MovieLister,
|
||||
movie_finder=finder,
|
||||
)
|
23
examples/miniapps/movie-lister/movies/finders.py
Normal file
23
examples/miniapps/movie-lister/movies/finders.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
"""Movie finders module."""
|
||||
|
||||
from typing import Callable, List
|
||||
|
||||
from .models import Movie
|
||||
from .storages import MovieStorage
|
||||
|
||||
|
||||
class MovieFinder:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
movie_factory: Callable[..., Movie],
|
||||
movie_storage: MovieStorage,
|
||||
) -> None:
|
||||
self._movie_factory = movie_factory
|
||||
self._movie_storage = movie_storage
|
||||
|
||||
def find_all(self) -> List[Movie]:
|
||||
return [
|
||||
self._movie_factory(*row)
|
||||
for row in self._movie_storage.get_all()
|
||||
]
|
|
@ -1,8 +1,8 @@
|
|||
"""Fixtures module."""
|
||||
|
||||
|
||||
MOVIES_SAMPLE_DATA = (
|
||||
MOVIES_SAMPLE_DATA = [
|
||||
('The Hunger Games: Mockingjay - Part 2', 2015, 'Francis Lawrence'),
|
||||
('The 33', 2015, 'Patricia Riggen'),
|
||||
('Star Wars: Episode VII - The Force Awakens', 2015, 'JJ Abrams'),
|
||||
)
|
||||
]
|
21
examples/miniapps/movie-lister/movies/listers.py
Normal file
21
examples/miniapps/movie-lister/movies/listers.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
"""Movie listers module."""
|
||||
|
||||
from .finders import MovieFinder
|
||||
|
||||
|
||||
class MovieLister:
|
||||
|
||||
def __init__(self, movie_finder: MovieFinder):
|
||||
self._movie_finder = movie_finder
|
||||
|
||||
def movies_directed_by(self, director):
|
||||
return [
|
||||
movie for movie in self._movie_finder.find_all()
|
||||
if movie.director == director
|
||||
]
|
||||
|
||||
def movies_released_in(self, year):
|
||||
return [
|
||||
movie for movie in self._movie_finder.find_all()
|
||||
if movie.year == year
|
||||
]
|
16
examples/miniapps/movie-lister/movies/models.py
Normal file
16
examples/miniapps/movie-lister/movies/models.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
"""Movie module."""
|
||||
|
||||
|
||||
class Movie:
|
||||
|
||||
def __init__(self, name: str, year: int, director: str):
|
||||
self.name = str(name)
|
||||
self.year = int(year)
|
||||
self.director = str(director)
|
||||
|
||||
def __repr__(self):
|
||||
return '{0}(name={1}, year={2}, director={3})'.format(
|
||||
self.__class__.__name__,
|
||||
repr(self.name),
|
||||
repr(self.year),
|
||||
repr(self.director))
|
55
examples/miniapps/movie-lister/movies/storages.py
Normal file
55
examples/miniapps/movie-lister/movies/storages.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
"""Movie storages module."""
|
||||
|
||||
import csv
|
||||
import sqlite3
|
||||
from typing import List, Tuple, Any
|
||||
|
||||
Row = Tuple[Any]
|
||||
|
||||
|
||||
class MovieStorage:
|
||||
|
||||
def load_all(self, movie_data: List[Row]):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_all(self) -> List[Row]:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class CsvMovieStorage(MovieStorage):
|
||||
|
||||
def __init__(self, options) -> None:
|
||||
self._csv_file_path = options.pop('path')
|
||||
self._delimiter = options.pop('delimiter')
|
||||
|
||||
def load_all(self, movie_data: List[Row]) -> None:
|
||||
with open(self._csv_file_path, 'w') as csv_file:
|
||||
csv.writer(csv_file, delimiter=self._delimiter).writerows(movie_data)
|
||||
|
||||
def get_all(self) -> List[Row]:
|
||||
with open(self._csv_file_path) as csv_file:
|
||||
csv_reader = csv.reader(csv_file, delimiter=self._delimiter)
|
||||
return [row for row in csv_reader]
|
||||
|
||||
|
||||
class SqliteMovieStorage(MovieStorage):
|
||||
|
||||
def __init__(self, options) -> None:
|
||||
self._database = sqlite3.connect(database=options.pop('path'))
|
||||
|
||||
def load_all(self, movie_data: List[Row]) -> None:
|
||||
with self._database as db:
|
||||
db.execute(
|
||||
'CREATE TABLE IF NOT EXISTS movies '
|
||||
'(name text, year int, director text)',
|
||||
)
|
||||
db.execute('DELETE FROM movies')
|
||||
db.executemany('INSERT INTO movies VALUES (?,?,?)', movie_data)
|
||||
|
||||
def get_all(self) -> List[Row]:
|
||||
with self._database as db:
|
||||
rows = db.execute(
|
||||
'SELECT name, year, director '
|
||||
'FROM movies',
|
||||
)
|
||||
return [row for row in rows]
|
55
examples/miniapps/movie-lister/movies/tests.py
Normal file
55
examples/miniapps/movie-lister/movies/tests.py
Normal file
|
@ -0,0 +1,55 @@
|
|||
"""Tests module."""
|
||||
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from .containers import ApplicationContainer
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def container():
|
||||
container = ApplicationContainer()
|
||||
container.config.from_dict({
|
||||
'storage': {
|
||||
'type': 'csv',
|
||||
'csv': {
|
||||
'path': '/fake-movies.csv',
|
||||
'delimiter': ',',
|
||||
},
|
||||
'sqlite': {
|
||||
'path': '/fake-movies.db',
|
||||
},
|
||||
},
|
||||
})
|
||||
return container
|
||||
|
||||
|
||||
def test_movies_directed_by(container):
|
||||
storage_mock = mock.Mock()
|
||||
storage_mock.get_all.return_value = [
|
||||
('The 33', 2015, 'Patricia Riggen'),
|
||||
('The Jungle Book', 2016, 'Jon Favreau'),
|
||||
]
|
||||
|
||||
with container.storage.override(storage_mock):
|
||||
lister = container.lister()
|
||||
movies = lister.movies_directed_by('Jon Favreau')
|
||||
|
||||
assert len(movies) == 1
|
||||
assert movies[0].name == 'The Jungle Book'
|
||||
|
||||
|
||||
def test_movies_released_in(container):
|
||||
storage_mock = mock.Mock()
|
||||
storage_mock.get_all.return_value = [
|
||||
('The 33', 2015, 'Patricia Riggen'),
|
||||
('The Jungle Book', 2016, 'Jon Favreau'),
|
||||
]
|
||||
|
||||
with container.storage.override(storage_mock):
|
||||
lister = container.lister()
|
||||
movies = lister.movies_released_in(2015)
|
||||
|
||||
assert len(movies) == 1
|
||||
assert movies[0].name == 'The 33'
|
4
examples/miniapps/movie-lister/requirements.txt
Normal file
4
examples/miniapps/movie-lister/requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
dependency-injector
|
||||
pyyaml
|
||||
pytest
|
||||
pytest-cov
|
|
@ -1,16 +0,0 @@
|
|||
A naive example of dependency injection in Python
|
||||
=================================================
|
||||
|
||||
Example implementation of dependency injection on Python from Martin Fowler's
|
||||
article about dependency injection and inversion of control:
|
||||
|
||||
http://www.martinfowler.com/articles/injection.html
|
||||
|
||||
|
||||
Instructions for running:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
python app_csv.py
|
||||
python app_db.py
|
||||
python app_db_csv.py
|
|
@ -1,49 +0,0 @@
|
|||
"""A naive example of dependency injection on Python.
|
||||
|
||||
Example implementation of dependency injection in Python from Martin Fowler's
|
||||
article about dependency injection and inversion of control:
|
||||
|
||||
http://www.martinfowler.com/articles/injection.html
|
||||
|
||||
This mini application uses ``movies`` library, that is configured to work with
|
||||
csv file movies database.
|
||||
"""
|
||||
|
||||
import movies
|
||||
import movies.finders
|
||||
|
||||
import example.db
|
||||
import example.main
|
||||
|
||||
import settings
|
||||
import fixtures
|
||||
|
||||
import dependency_injector.containers as containers
|
||||
import dependency_injector.providers as providers
|
||||
|
||||
|
||||
@containers.override(movies.MoviesModule)
|
||||
class MyMoviesModule(containers.DeclarativeContainer):
|
||||
"""IoC container for overriding movies module component providers."""
|
||||
|
||||
finder = providers.Factory(movies.finders.CsvMovieFinder,
|
||||
csv_file_path=settings.MOVIES_CSV_PATH,
|
||||
delimiter=',',
|
||||
**movies.MoviesModule.finder.kwargs)
|
||||
|
||||
|
||||
class CsvApplication(containers.DeclarativeContainer):
|
||||
"""IoC container of csv application component providers."""
|
||||
|
||||
main = providers.Callable(example.main.main,
|
||||
movie_lister=movies.MoviesModule.lister)
|
||||
|
||||
init_db = providers.Callable(example.db.init_csv,
|
||||
movies_data=fixtures.MOVIES_SAMPLE_DATA,
|
||||
csv_file_path=settings.MOVIES_CSV_PATH,
|
||||
delimiter=',')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
CsvApplication.init_db()
|
||||
CsvApplication.main()
|
|
@ -1,55 +0,0 @@
|
|||
"""A naive example of dependency injection on Python.
|
||||
|
||||
Example implementation of dependency injection in Python from Martin Fowler's
|
||||
article about dependency injection and inversion of control:
|
||||
|
||||
http://www.martinfowler.com/articles/injection.html
|
||||
|
||||
This mini application uses ``movies`` library, that is configured to work with
|
||||
sqlite movies database.
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
|
||||
import movies
|
||||
import movies.finders
|
||||
|
||||
import example.db
|
||||
import example.main
|
||||
|
||||
import settings
|
||||
import fixtures
|
||||
|
||||
import dependency_injector.containers as containers
|
||||
import dependency_injector.providers as providers
|
||||
|
||||
|
||||
class ResourcesModule(containers.DeclarativeContainer):
|
||||
"""IoC container of application resource providers."""
|
||||
|
||||
database = providers.Singleton(sqlite3.connect, settings.MOVIES_DB_PATH)
|
||||
|
||||
|
||||
@containers.override(movies.MoviesModule)
|
||||
class MyMoviesModule(containers.DeclarativeContainer):
|
||||
"""IoC container for overriding movies module component providers."""
|
||||
|
||||
finder = providers.Factory(movies.finders.SqliteMovieFinder,
|
||||
database=ResourcesModule.database,
|
||||
**movies.MoviesModule.finder.kwargs)
|
||||
|
||||
|
||||
class DbApplication(containers.DeclarativeContainer):
|
||||
"""IoC container of database application component providers."""
|
||||
|
||||
main = providers.Callable(example.main.main,
|
||||
movie_lister=movies.MoviesModule.lister)
|
||||
|
||||
init_db = providers.Callable(example.db.init_sqlite,
|
||||
movies_data=fixtures.MOVIES_SAMPLE_DATA,
|
||||
database=ResourcesModule.database)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
DbApplication.init_db()
|
||||
DbApplication.main()
|
|
@ -1,80 +0,0 @@
|
|||
"""A naive example of dependency injection on Python.
|
||||
|
||||
Example implementation of dependency injection in Python from Martin Fowler's
|
||||
article about dependency injection and inversion of control:
|
||||
|
||||
http://www.martinfowler.com/articles/injection.html
|
||||
|
||||
This mini application uses ``movies`` library, that is configured to work with
|
||||
sqlite movies database and csv file movies database.
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
|
||||
import movies
|
||||
import movies.finders
|
||||
|
||||
import example.db
|
||||
import example.main
|
||||
|
||||
import settings
|
||||
import fixtures
|
||||
|
||||
import dependency_injector.containers as containers
|
||||
import dependency_injector.providers as providers
|
||||
|
||||
|
||||
class ResourcesModule(containers.DeclarativeContainer):
|
||||
"""IoC container of application resource providers."""
|
||||
|
||||
database = providers.Singleton(sqlite3.connect, settings.MOVIES_DB_PATH)
|
||||
|
||||
|
||||
@containers.copy(movies.MoviesModule)
|
||||
class DbMoviesModule(movies.MoviesModule):
|
||||
"""IoC container for overriding movies module component providers."""
|
||||
|
||||
finder = providers.Factory(movies.finders.SqliteMovieFinder,
|
||||
database=ResourcesModule.database,
|
||||
**movies.MoviesModule.finder.kwargs)
|
||||
|
||||
|
||||
@containers.copy(movies.MoviesModule)
|
||||
class CsvMoviesModule(movies.MoviesModule):
|
||||
"""IoC container for overriding movies module component providers."""
|
||||
|
||||
finder = providers.Factory(movies.finders.CsvMovieFinder,
|
||||
csv_file_path=settings.MOVIES_CSV_PATH,
|
||||
delimiter=',',
|
||||
**movies.MoviesModule.finder.kwargs)
|
||||
|
||||
|
||||
class DbApplication(containers.DeclarativeContainer):
|
||||
"""IoC container of database application component providers."""
|
||||
|
||||
main = providers.Callable(example.main.main,
|
||||
movie_lister=DbMoviesModule.lister)
|
||||
|
||||
init_db = providers.Callable(example.db.init_sqlite,
|
||||
movies_data=fixtures.MOVIES_SAMPLE_DATA,
|
||||
database=ResourcesModule.database)
|
||||
|
||||
|
||||
class CsvApplication(containers.DeclarativeContainer):
|
||||
"""IoC container of csv application component providers."""
|
||||
|
||||
main = providers.Callable(example.main.main,
|
||||
movie_lister=CsvMoviesModule.lister)
|
||||
|
||||
init_db = providers.Callable(example.db.init_csv,
|
||||
movies_data=fixtures.MOVIES_SAMPLE_DATA,
|
||||
csv_file_path=settings.MOVIES_CSV_PATH,
|
||||
delimiter=',')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
DbApplication.init_db()
|
||||
DbApplication.main()
|
||||
|
||||
CsvApplication.init_db()
|
||||
CsvApplication.main()
|
|
@ -1,3 +0,0 @@
|
|||
The Hunger Games: Mockingjay - Part 2,2015,Francis Lawrence
|
||||
The 33,2015,Patricia Riggen
|
||||
Star Wars: Episode VII - The Force Awakens,2015,JJ Abrams
|
|
|
@ -1 +0,0 @@
|
|||
"""Example top-level package."""
|
|
@ -1,35 +0,0 @@
|
|||
"""Example database module."""
|
||||
|
||||
import csv
|
||||
|
||||
|
||||
def init_sqlite(movies_data, database):
|
||||
"""Initialize sqlite3 movies database.
|
||||
|
||||
:param movies_data: Data about movies
|
||||
:type movies_data: tuple[tuple]
|
||||
|
||||
:param database: Connection to sqlite database with movies data
|
||||
:type database: sqlite3.Connection
|
||||
"""
|
||||
with database:
|
||||
database.execute('CREATE TABLE IF NOT EXISTS movies '
|
||||
'(name text, year int, director text)')
|
||||
database.execute('DELETE FROM movies')
|
||||
database.executemany('INSERT INTO movies VALUES (?,?,?)', movies_data)
|
||||
|
||||
|
||||
def init_csv(movies_data, csv_file_path, delimiter):
|
||||
"""Initialize csv movies database.
|
||||
|
||||
:param movies_data: Data about movies
|
||||
:type movies_data: tuple[tuple]
|
||||
|
||||
:param csv_file_path: Path to csv file with movies data
|
||||
:type csv_file_path: str
|
||||
|
||||
:param delimiter: Csv file's delimiter
|
||||
:type delimiter: str
|
||||
"""
|
||||
with open(csv_file_path, 'w') as csv_file:
|
||||
csv.writer(csv_file, delimiter=delimiter).writerows(movies_data)
|
|
@ -1,17 +0,0 @@
|
|||
"""Example main module."""
|
||||
|
||||
|
||||
def main(movie_lister):
|
||||
"""Run application.
|
||||
|
||||
This program prints info about all movies that were directed by different
|
||||
persons and then prints all movies that were released in 2015.
|
||||
|
||||
:param movie_lister: Movie lister instance
|
||||
:type movie_lister: movies.listers.MovieLister
|
||||
"""
|
||||
print(movie_lister.movies_directed_by('Francis Lawrence'))
|
||||
print(movie_lister.movies_directed_by('Patricia Riggen'))
|
||||
print(movie_lister.movies_directed_by('JJ Abrams'))
|
||||
|
||||
print(movie_lister.movies_released_in(2015))
|
|
@ -1,32 +0,0 @@
|
|||
"""Movies package.
|
||||
|
||||
Top-level package of movies library. This package contains IoC container of
|
||||
movies module component providers - ``MoviesModule``. It is recommended to use
|
||||
movies library functionality by fetching required instances from
|
||||
``MoviesModule`` providers.
|
||||
|
||||
``MoviesModule.finder`` is a factory that provides abstract component
|
||||
``finders.MovieFinder``. This provider should be overridden by provider of
|
||||
concrete finder implementation in terms of library configuration.
|
||||
|
||||
Each of ``MoviesModule`` providers could be overridden.
|
||||
"""
|
||||
|
||||
import movies.finders
|
||||
import movies.listers
|
||||
import movies.models
|
||||
|
||||
import dependency_injector.containers as containers
|
||||
import dependency_injector.providers as providers
|
||||
|
||||
|
||||
class MoviesModule(containers.DeclarativeContainer):
|
||||
"""IoC container of movies module component providers."""
|
||||
|
||||
movie = providers.Factory(movies.models.Movie)
|
||||
|
||||
finder = providers.AbstractFactory(movies.finders.MovieFinder,
|
||||
movie_model=movie.provider)
|
||||
|
||||
lister = providers.Factory(movies.listers.MovieLister,
|
||||
movie_finder=finder)
|
|
@ -1,87 +0,0 @@
|
|||
"""Movie finders module.
|
||||
|
||||
This module contains all finder implementations.
|
||||
"""
|
||||
|
||||
import csv
|
||||
|
||||
|
||||
class MovieFinder:
|
||||
"""Movie finder component.
|
||||
|
||||
Movie finder component is responsible for fetching movies data from
|
||||
various storage.
|
||||
"""
|
||||
|
||||
def __init__(self, movie_model):
|
||||
"""Initialize instance.
|
||||
|
||||
:param movie_model: Movie model's factory
|
||||
:type movie_model: movies.models.Movie
|
||||
"""
|
||||
self._movie_model = movie_model
|
||||
|
||||
def find_all(self):
|
||||
"""Return all found movies.
|
||||
|
||||
:rtype: list[movies.models.Movie]
|
||||
:return: List of movie instances.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class CsvMovieFinder(MovieFinder):
|
||||
"""Movie finder that fetches movies data from csv file."""
|
||||
|
||||
def __init__(self, movie_model, csv_file_path, delimiter):
|
||||
"""Initialize instance.
|
||||
|
||||
:param movie_model: Movie model's factory
|
||||
:type movie_model: movies.models.Movie
|
||||
|
||||
:param csv_file_path: Path to csv file with movies data
|
||||
:type csv_file_path: str
|
||||
|
||||
:param delimiter: Csv file's delimiter
|
||||
:type delimiter: str
|
||||
"""
|
||||
self._csv_file_path = csv_file_path
|
||||
self._delimiter = delimiter
|
||||
super().__init__(movie_model)
|
||||
|
||||
def find_all(self):
|
||||
"""Return all found movies.
|
||||
|
||||
:rtype: list[movies.models.Movie]
|
||||
:return: List of movie instances.
|
||||
"""
|
||||
with open(self._csv_file_path) as csv_file:
|
||||
csv_reader = csv.reader(csv_file, delimiter=self._delimiter)
|
||||
return [self._movie_model(*row) for row in csv_reader]
|
||||
|
||||
|
||||
class SqliteMovieFinder(MovieFinder):
|
||||
"""Movie finder that fetches movies data from sqlite database."""
|
||||
|
||||
def __init__(self, movie_model, database):
|
||||
"""Initialize instance.
|
||||
|
||||
:param movie_model: Movie model's factory
|
||||
:type movie_model: (object) -> movies.models.Movie
|
||||
|
||||
:param database: Connection to sqlite database with movies data
|
||||
:type database: sqlite3.Connection
|
||||
"""
|
||||
self._database = database
|
||||
super().__init__(movie_model)
|
||||
|
||||
def find_all(self):
|
||||
"""Return all found movies.
|
||||
|
||||
:rtype: list[movies.models.Movie]
|
||||
:return: List of movie instances.
|
||||
"""
|
||||
with self._database:
|
||||
rows = self._database.execute('SELECT name, year, director '
|
||||
'FROM movies')
|
||||
return [self._movie_model(*row) for row in rows]
|
|
@ -1,44 +0,0 @@
|
|||
"""Movie listers module.
|
||||
|
||||
This module contains all lister implementations.
|
||||
"""
|
||||
|
||||
|
||||
class MovieLister:
|
||||
"""Movie lister component.
|
||||
|
||||
Movie lister component provides several methods for filtering movies by
|
||||
specific criteria.
|
||||
"""
|
||||
|
||||
def __init__(self, movie_finder):
|
||||
"""Initialize instance.
|
||||
|
||||
:param movie_finder: Movie finder instance
|
||||
:type movie_finder: movies.finders.MovieFinder
|
||||
"""
|
||||
self._movie_finder = movie_finder
|
||||
|
||||
def movies_directed_by(self, director):
|
||||
"""Return list of movies that were directed by certain person.
|
||||
|
||||
:param director: Director's name
|
||||
:type director: str
|
||||
|
||||
:rtype: list[movies.models.Movie]
|
||||
:return: List of movie instances.
|
||||
"""
|
||||
return [movie for movie in self._movie_finder.find_all()
|
||||
if movie.director == director]
|
||||
|
||||
def movies_released_in(self, year):
|
||||
"""Return list of movies that were released in certain year.
|
||||
|
||||
:param year: Release year
|
||||
:type year: int
|
||||
|
||||
:rtype: list[movies.models.Movie]
|
||||
:return: List of movie instances.
|
||||
"""
|
||||
return [movie for movie in self._movie_finder.find_all()
|
||||
if movie.year == year]
|
|
@ -1,36 +0,0 @@
|
|||
"""Movie models module.
|
||||
|
||||
This module contains all model implementations.
|
||||
"""
|
||||
|
||||
|
||||
class Movie:
|
||||
"""Base movie model."""
|
||||
|
||||
def __init__(self, name, year, director):
|
||||
"""Initialize instance.
|
||||
|
||||
:param name: Movie's name
|
||||
:type name: str
|
||||
|
||||
:param year: Year, when movie was released
|
||||
:type year: int
|
||||
|
||||
:param director: Name of person, that directed the movie
|
||||
:type director: str
|
||||
"""
|
||||
self.name = str(name)
|
||||
self.year = int(year)
|
||||
self.director = str(director)
|
||||
|
||||
def __repr__(self):
|
||||
"""Return string representation of movie instance.
|
||||
|
||||
:rtype: str
|
||||
:return: Movie's string representation.
|
||||
"""
|
||||
return '{0}(name={1}, year={2}, director={3})'.format(
|
||||
self.__class__.__name__,
|
||||
repr(self.name),
|
||||
repr(self.year),
|
||||
repr(self.director))
|
|
@ -1,11 +0,0 @@
|
|||
"""Settings module.
|
||||
|
||||
This module contains application's settings and constants.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
DATA_DIR = os.path.abspath(os.path.dirname(__file__) + '/data')
|
||||
MOVIES_CSV_PATH = DATA_DIR + '/movies.csv'
|
||||
MOVIES_DB_PATH = DATA_DIR + '/movies.db'
|
Loading…
Reference in New Issue
Block a user