From 89c884d7645605f3779826606640ecec036d9af0 Mon Sep 17 00:00:00 2001
From: Pavel Kirilin <win10@list.ru>
Date: Wed, 18 Aug 2021 12:50:37 +0400
Subject: [PATCH] Updated template.

Signed-off-by: Pavel Kirilin <win10@list.ru>
---
 README.md                                     |  64 +-
 fastapi_template/__main__.py                  |  37 ++
 fastapi_template/cli.py                       | 172 ++++++
 fastapi_template/input_model.py               |  42 ++
 fastapi_template/main.py                      |  24 -
 fastapi_template/template/cookiecutter.json   |  46 +-
 .../template/hooks/post_gen_project.py        |  20 +-
 .../.dockerignore                             | 143 +++++
 .../.editorconfig                             |  31 +
 .../{{cookiecutter.project_name}}/.env        |   1 +
 .../{{cookiecutter.project_name}}/.flake8     |  89 +++
 .../{{cookiecutter.project_name}}/.gitignore  |   6 +-
 .../.gitlab-ci.yml                            |  72 ++-
 .../.pre-commit-config.yaml                   |  94 +--
 .../{{cookiecutter.project_name}}/Dockerfile  | 105 ----
 .../{{cookiecutter.project_name}}/README.md   |  66 +--
 .../{{cookiecutter.project_name}}/alembic.ini |  54 +-
 .../conditional_files.json                    |  81 ++-
 .../{{cookiecutter.project_name}}/deploy.sh   |  14 -
 .../deploy/Dockerfile                         |  32 +
 .../deploy/docker-compose.yml                 |  45 ++
 .../deploy/kube/app.yml                       |  57 ++
 .../deploy/kube/db.yml                        |  33 ++
 .../deploy/kube/redis.yml                     |  33 ++
 .../docker-compose.prod.yml                   |  82 ---
 .../docker-compose.yml                        |  90 ---
 .../{{cookiecutter.project_name}}/envs/.env   |  17 -
 .../envs/example.env                          |  18 -
 .../envs/test.env                             |  18 -
 .../{{cookiecutter.project_name}}/main.py     |  45 --
 .../migrations/env.py                         |  96 ---
 .../7ae297ab5ac1_created_dummy_model.py       |  42 --
 .../pyproject.toml                            |  97 +++-
 .../{{cookiecutter.project_name}}/pytest.ini  |   4 -
 .../scheduler.py                              |  19 -
 .../src/api/__init__.py                       |  28 -
 .../src/api/dummy_db/__init__.py              |   3 -
 .../src/api/dummy_db/routes.py                |  88 ---
 .../src/api/dummy_db/schema.py                |  44 --
 .../src/api/httpbin/__init__.py               |   3 -
 .../src/api/httpbin/routes.py                 |  17 -
 .../src/api/httpbin/schema.py                 |   0
 .../src/api/redis_api/__init__.py             |   3 -
 .../src/api/redis_api/routes.py               |  32 -
 .../src/api/redis_api/schema.py               |  11 -
 .../src/exceptions.py                         |  23 -
 .../src/models/__init__.py                    |   5 -
 .../src/models/dummy_db_model.py              |  76 ---
 .../src/server.py                             |  70 ---
 .../src/services/__init__.py                  |   0
 .../src/services/db/__init__.py               |   5 -
 .../src/services/db/base.py                   | 105 ----
 .../src/services/db/db_meta.py                |   3 -
 .../src/services/db/engine.py                 |  41 --
 .../src/services/db/session.py                |  61 --
 .../src/services/elastic/__init__.py          |   6 -
 .../src/services/elastic/client.py            |   4 -
 .../src/services/elastic/mixin.py             | 115 ----
 .../src/services/elastic/schema.py            |  15 -
 .../src/services/httpbin/__init__.py          |   0
 .../src/services/httpbin/client.py            |  60 --
 .../src/services/httpbin/schema.py            |  20 -
 .../src/services/redis.py                     |  50 --
 .../src/settings.py                           |  44 --
 .../src/utils.py                              |   0
 ...iecutter.project_name }}_scheduler.service |  15 -
 ...okiecutter.project_name }}_service.service |  15 -
 .../tests/__init__.py                         |  13 -
 .../tests/conftest.py                         |  93 ---
 .../tests/dummy_db_test.py                    |  39 --
 .../{{cookiecutter.project_name}}/__init__.py |   1 +
 .../{{cookiecutter.project_name}}/__main__.py |  19 +
 .../{{cookiecutter.project_name}}/conftest.py |  54 ++
 .../{{cookiecutter.project_name}}/db/base.py  |  20 +
 .../db/dao/__init__.py                        |   1 +
 .../db/dao/dummy_dao.py                       |  35 ++
 .../db/dependencies.py                        |  19 +
 .../{{cookiecutter.project_name}}/db/meta.py  |   3 +
 .../db/migrations/README.md}                  |   0
 .../db/migrations/__init__.py                 |   1 +
 .../db/migrations/env.py                      |  85 +++
 .../db}/migrations/script.py.mako             |   4 +-
 .../versions/2021-08-16-16-53_819cbf6e030b.py |  22 +
 .../versions/2021-08-16-16-55_2b7380507a71.py |  32 +
 .../db/migrations/versions}/__init__.py       |   0
 .../db/models/__init__.py                     |  14 +
 .../db/models/dummy_model.py                  |  13 +
 .../services/redis/dependency.py              |  20 +
 .../{{cookiecutter.project_name}}/settings.py |  94 +++
 .../tests/__init__.py                         |   1 +
 .../tests/test_fastapitestproject.py          |   8 +
 .../web/api/__init__.py                       |   1 +
 .../web/api/dummy/__init__.py                 |   4 +
 .../web/api/dummy/schema.py                   |  21 +
 .../web/api/dummy/views.py                    |  41 ++
 .../web/api/echo/__init__.py                  |   4 +
 .../web/api/echo/schema.py                    |   7 +
 .../web/api/echo/views.py                     |  18 +
 .../web/api/redis/__init__.py                 |   4 +
 .../web/api/redis/schema.py                   |  10 +
 .../web/api/redis/views.py                    |  41 ++
 .../web/api/router.py                         |  19 +
 .../web/application.py                        |  24 +
 .../web/lifetime.py                           |  94 +++
 images/ui-example.png                         | Bin 0 -> 23550 bytes
 poetry.lock                                   | 549 +++++++++++-------
 pyproject.toml                                |  12 +-
 107 files changed, 2108 insertions(+), 2183 deletions(-)
 create mode 100644 fastapi_template/__main__.py
 create mode 100644 fastapi_template/cli.py
 create mode 100644 fastapi_template/input_model.py
 delete mode 100644 fastapi_template/main.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/.dockerignore
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/.editorconfig
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/.env
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/.flake8
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/Dockerfile
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/deploy.sh
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/deploy/Dockerfile
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.yml
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/app.yml
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/db.yml
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/redis.yml
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/docker-compose.prod.yml
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/docker-compose.yml
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/envs/.env
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/envs/example.env
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/envs/test.env
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/main.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/migrations/env.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/migrations/versions/7ae297ab5ac1_created_dummy_model.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/pytest.ini
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/scheduler.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/api/__init__.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/__init__.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/routes.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/schema.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/api/httpbin/__init__.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/api/httpbin/routes.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/api/httpbin/schema.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/__init__.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/routes.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/schema.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/exceptions.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/models/__init__.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/models/dummy_db_model.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/server.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/__init__.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/__init__.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/base.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/db_meta.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/engine.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/session.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/__init__.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/client.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/mixin.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/schema.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/httpbin/__init__.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/httpbin/client.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/httpbin/schema.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/services/redis.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/settings.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/src/utils.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/systemd/{{ cookiecutter.project_name }}_scheduler.service
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/systemd/{{ cookiecutter.project_name }}_service.service
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/tests/__init__.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/tests/conftest.py
 delete mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/tests/dummy_db_test.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/__init__.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/__main__.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/conftest.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/base.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dao/__init__.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dao/dummy_dao.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dependencies.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/meta.py
 rename fastapi_template/template/{{cookiecutter.project_name}}/{migrations/__init__.py => {{cookiecutter.project_name}}/db/migrations/README.md} (100%)
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/__init__.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/env.py
 rename fastapi_template/template/{{cookiecutter.project_name}}/{ => {{cookiecutter.project_name}}/db}/migrations/script.py.mako (90%)
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/versions/2021-08-16-16-53_819cbf6e030b.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/versions/2021-08-16-16-55_2b7380507a71.py
 rename fastapi_template/template/{{cookiecutter.project_name}}/{src => {{cookiecutter.project_name}}/db/migrations/versions}/__init__.py (100%)
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/models/__init__.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/models/dummy_model.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/services/redis/dependency.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/settings.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/__init__.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/test_fastapitestproject.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/__init__.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/__init__.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/schema.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/views.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/__init__.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/schema.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/views.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/__init__.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/schema.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/views.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/router.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/application.py
 create mode 100644 fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py
 create mode 100644 images/ui-example.png

diff --git a/README.md b/README.md
index ac64d67..b6a4493 100644
--- a/README.md
+++ b/README.md
@@ -5,24 +5,66 @@
 <div><i>Fast and flexible general-purpose template for your API.</i></div>
 </div>
 
+<div align="center">
+<img src="https://raw.githubusercontent.com/s3rius/FastAPI-template/master/images/ui-example.png" width=700>
+<div><i>With text user interface.</i></div>
+</div>
 
 ## Usage
-⚠️ [Git](https://git-scm.com/downloads), [Python](https://www.python.org/), and [Docker-compose](https://docs.docker.com/compose/install/) must be installed and accessible ⚠️
+
+⚠️ [Git](https://git-scm.com/downloads), [Python](https://www.python.org/) and [Poetry](https://python-poetry.org/) must be installed and accessible ⚠️
 
 ```bash
 python3 -m pip install fastapi_template
-fastapi_template
-# Answer prompts questions
-# ???
+python3 -m fastapi_template
+# or fastapi_template
+# Answer all the questions
 # 🍪 Enjoy your new project 🍪
 cd new_project
-docker-compose up --build
+docker-compose -f deploy/docker-compose.yml --project-directory . up --build
+```
+
+If you want to install in from sources then try this:
+```shell
+python3 -m pip install poetry
+python3 -m pip install .
+python3 -m fastapi_template
 ```
 
 ## Features
-Currently supported features:
-- redis
-- systemd units
-- Example (dummy) SQLAlchemy model
-- Elastic Search support
-- Scheduler support
+
+Template is made with SQLAlchemy14 and uses sqlalchemy orm and sessions,
+instead of raw drivers.
+
+It has minimum to start new excellent project.
+
+Generator features:
+- Different databases to choose from.
+- Alembic integration;
+- redis support;
+- CI\CD (Currently only gitlab-ci);
+- Kubernetes config.
+
+This project can handle arguments passed through command line.
+
+```shell
+$ python -m fastapi_template --help
+
+usage: FastAPI template [-h] [--name PROJECT_NAME]
+                        [--description PROJECT_DESCRIPTION]
+                        [--db {none,sqlite,mysql,postgresql}] [--ci] [--redis]
+                        [--alembic] [--kube] [--force]
+
+optional arguments:
+  -h, --help            show this help message and exit
+  --name PROJECT_NAME   Name of your awesome project
+  --description PROJECT_DESCRIPTION
+                        Project description
+  --db {none,sqlite,mysql,postgresql}
+                        Database
+  --ci                  Add CI/CD support
+  --redis               Add redis support
+  --alembic             Add alembic support
+  --kube                Add kubernetes configs
+  --force               Owerrite directory if exists
+```
diff --git a/fastapi_template/__main__.py b/fastapi_template/__main__.py
new file mode 100644
index 0000000..8e52abe
--- /dev/null
+++ b/fastapi_template/__main__.py
@@ -0,0 +1,37 @@
+from pathlib import Path
+
+from cookiecutter.exceptions import FailedHookException, OutputDirExistsException
+from cookiecutter.main import cookiecutter
+from termcolor import cprint
+
+from fastapi_template.cli import get_context
+from fastapi_template.input_model import BuilderContext
+
+script_dir = Path(__file__).parent
+
+
+def main():
+    try:
+        context = get_context()
+    except KeyboardInterrupt:
+        print("Goodbye!")
+        return
+    try:
+        cookiecutter(
+            template=f"{script_dir}/template",
+            extra_context=context.dict(),
+            default_config=BuilderContext().dict(),
+            no_input=True,
+            overwrite_if_exists=context.force,
+        )
+    except (FailedHookException, OutputDirExistsException) as exc:
+        if isinstance(exc, OutputDirExistsException):
+            cprint("Directory with such name already exists!", "red")
+        return
+    cprint(
+        "Project successfully generated. You can read information about usage in README.md"
+    )
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fastapi_template/cli.py b/fastapi_template/cli.py
new file mode 100644
index 0000000..fc9054d
--- /dev/null
+++ b/fastapi_template/cli.py
@@ -0,0 +1,172 @@
+import re
+from argparse import ArgumentParser
+from operator import attrgetter
+
+from prompt_toolkit import prompt
+from prompt_toolkit.document import Document
+from prompt_toolkit.shortcuts import checkboxlist_dialog, radiolist_dialog
+from prompt_toolkit.validation import ValidationError, Validator
+
+from fastapi_template.input_model import BuilderContext, Database, DatabaseType
+
+
+class SnakeCaseValidator(Validator):
+    def validate(self, document: Document):
+        text = document.text
+        if not text or re.fullmatch(r"[a-zA-Z][\w\_\d]*", text) is None:
+            raise ValidationError(message="Must be a valid snake_case name.")
+
+
+DB_INFO = {
+    DatabaseType.none: Database(
+        name="none",
+        image="none",
+        driver=None,
+        port=None,
+    ),
+    DatabaseType.postgresql: Database(
+        name=DatabaseType.postgresql.value,
+        image="postgres:13.4-buster",
+        driver="postgresql+asyncpg",
+        port=5432,
+    ),
+    DatabaseType.mysql: Database(
+        name=DatabaseType.mysql.value,
+        image="bitnami/mysql:8.0.26",
+        driver="mysql+aiomysql",
+        port=3306,
+    ),
+    DatabaseType.sqlite: Database(
+        name=DatabaseType.sqlite.value, image=None, driver="sqlite+aiosqlite", port=None
+    ),
+}
+
+
+def parse_args():
+    parser = ArgumentParser(
+        prog="FastAPI template",
+    )
+    parser.add_argument(
+        "--name",
+        type=str,
+        dest="project_name",
+        help="Name of your awesome project",
+    )
+    parser.add_argument(
+        "--description",
+        type=str,
+        dest="project_description",
+        help="Project description",
+    )
+    parser.add_argument(
+        "--db",
+        help="Database",
+        type=DatabaseType,
+        choices=list(DatabaseType),
+        default=None,
+        dest="db",
+    )
+    parser.add_argument(
+        "--ci",
+        help="Add CI/CD support",
+        action="store_true",
+        default=None,
+        dest="enable_ci",
+    )
+    parser.add_argument(
+        "--redis",
+        help="Add redis support",
+        action="store_true",
+        default=None,
+        dest="enable_redis",
+    )
+    parser.add_argument(
+        "--alembic",
+        help="Add alembic support",
+        action="store_true",
+        default=None,
+        dest="enable_alembic",
+    )
+    parser.add_argument(
+        "--kube",
+        help="Add kubernetes configs",
+        action="store_true",
+        default=None,
+        dest="enable_kube",
+    )
+    parser.add_argument(
+        "--force",
+        help="Owerrite directory if it exists",
+        action="store_true",
+        default=False,
+        dest="force",
+    )
+
+    return parser.parse_args()
+
+
+def ask_features(current_context: BuilderContext) -> BuilderContext:
+    features = {
+        "Redis support": {
+            "name": "enable_redis",
+            "value": current_context.enable_redis,
+        },
+        "CI/CD": {
+            "name": "enable_ci",
+            "value": current_context.enable_ci,
+        },
+        "Kubernetes": {
+            "name": "enable_kube",
+            "value": current_context.enable_kube,
+        },
+    }
+    if current_context.db != DatabaseType.none:
+        features["Alembic migrations"] = {
+            "name": "enable_alembic",
+            "value": current_context.enable_alembic,
+        }
+    checkbox_values = []
+    for feature_name, feature in features.items():
+        if feature["value"] is None:
+            setattr(current_context, feature["name"], False)
+            checkbox_values.append((feature["name"], feature_name))
+    if checkbox_values:
+        results = checkboxlist_dialog(
+            title="Features",
+            text="What features do you wanna add?",
+            values=checkbox_values,
+        ).run()
+        if results is None:
+            raise KeyboardInterrupt()
+        for feature in results:
+            setattr(current_context, feature, True)
+    return current_context
+
+
+def read_user_input(current_context: BuilderContext) -> BuilderContext:
+    if current_context.project_name is None:
+        current_context.project_name = prompt(
+            "Project name: ", validator=SnakeCaseValidator()
+        )
+    if current_context.project_description is None:
+        current_context.project_description = prompt("Project description: ")
+    if current_context.db is None:
+        current_context.db = radiolist_dialog(
+            "Databases",
+            text="Which database do you want?",
+            values=[(db, db.value) for db in list(DatabaseType)],
+        ).run()
+        if current_context.db is None:
+            raise KeyboardInterrupt()
+        if current_context.db == DatabaseType.none:
+            current_context.enable_alembic = False
+    ask_features(current_context)
+    return current_context
+
+
+def get_context() -> BuilderContext:
+    args = parse_args()
+    context = BuilderContext.from_orm(args)
+    context = read_user_input(context)
+    context.db_info = DB_INFO[context.db]
+    return context
diff --git a/fastapi_template/input_model.py b/fastapi_template/input_model.py
new file mode 100644
index 0000000..1d2eb93
--- /dev/null
+++ b/fastapi_template/input_model.py
@@ -0,0 +1,42 @@
+import enum
+from typing import Optional
+
+from pydantic import BaseModel
+
+
+@enum.unique
+class DatabaseType(enum.Enum):
+    none = "none"
+    sqlite = "sqlite"
+    mysql = "mysql"
+    postgresql = "postgresql"
+
+
+@enum.unique
+class CIType(enum.Enum):
+    none = "none"
+    gitlab_ci = "gitlab"
+
+
+class Database(BaseModel):
+    name: str
+    image: Optional[str]
+    driver: Optional[str]
+    port: Optional[int]
+
+
+class BuilderContext(BaseModel):
+    """Options for project generation."""
+
+    project_name: Optional[str]
+    project_description: Optional[str]
+    db: Optional[DatabaseType]
+    db_info: Optional[Database]
+    enable_redis: Optional[bool]
+    enable_ci: Optional[bool]
+    enable_alembic: Optional[bool]
+    enable_kube: Optional[bool]
+    force: bool = False
+
+    class Config:
+        orm_mode = True
diff --git a/fastapi_template/main.py b/fastapi_template/main.py
deleted file mode 100644
index b6b3063..0000000
--- a/fastapi_template/main.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from pathlib import Path
-
-from cookiecutter.exceptions import FailedHookException, OutputDirExistsException
-from cookiecutter.main import cookiecutter
-from termcolor import cprint
-
-script_dir = Path(__file__).parent
-
-
-def main():
-    try:
-        cookiecutter(template=f"{script_dir}/template")
-    except (FailedHookException, OutputDirExistsException) as exc:
-        if isinstance(exc, OutputDirExistsException):
-            cprint(
-                "Directory with such name already exists!",
-                "red"
-            )
-        return
-    cprint("Project successfully generated. You can read information about usage in README.md")
-
-
-if __name__ == "__main__":
-    main()
diff --git a/fastapi_template/template/cookiecutter.json b/fastapi_template/template/cookiecutter.json
index 6bc0e64..469ca15 100644
--- a/fastapi_template/template/cookiecutter.json
+++ b/fastapi_template/template/cookiecutter.json
@@ -1,29 +1,25 @@
 {
-  "project_name": "new_fastapi_project",
-  "full_name": "Your name",
-  "email": "win10@list.ru",
-  "project_description": "",
-  "default_port": 8401,
-  "add_redis": [
-    true,
-    false
-  ],
-  "add_systemd": [
-    true,
-    false
-  ],
-  "add_dummy_model": [
-    true,
-    false
-  ],
-  "add_elastic_search": [
-    true,
-    false
-  ],
-  "add_scheduler": [
-    true,
-    false
-  ],
+  "project_name": {
+    "type": "string"
+  },
+  "project_description": {
+    "type": "string"
+  },
+  "db_info": {
+    "type": "dict"
+  },
+  "enable_redis": {
+    "type": "bool"
+  },
+  "enable_ci": {
+    "type": "bool"
+  },
+  "enable_alembic": {
+    "type": "bool"
+  },
+  "enable_kube": {
+    "type": "bool"
+  },
   "_extensions": [
     "cookiecutter.extensions.RandomStringExtension"
   ]
diff --git a/fastapi_template/template/hooks/post_gen_project.py b/fastapi_template/template/hooks/post_gen_project.py
index ccb10c5..a9bf5aa 100644
--- a/fastapi_template/template/hooks/post_gen_project.py
+++ b/fastapi_template/template/hooks/post_gen_project.py
@@ -2,12 +2,13 @@
 import json
 import os
 import shutil
+import subprocess
 from argparse import Namespace
 
 import pre_commit.constants as pre_commit_constants
 import pre_commit.main as pre_commit
 from pygit2 import init_repository
-from termcolor import cprint
+from termcolor import cprint, colored
 
 MANIFEST = "conditional_files.json"
 
@@ -24,13 +25,17 @@ def delete_resource(resource):
 def delete_resources_for_disabled_features():
     with open(MANIFEST) as manifest_file:
         manifest = json.load(manifest_file)
-        for feature in manifest['features']:
-            if not feature['enabled'] == "true":
-                print("removing resources for disabled feature {}...".format(feature['name']))
+        for feature_name, feature in manifest.items():
+            if feature['enabled'].lower() != "true":
+                text = "{} resources for disabled feature {}...".format(
+                    colored("Removing", color="red"),
+                    colored(feature_name, color="magenta", attrs=['underline'])
+                )
+                print(text)
                 for resource in feature['resources']:
                     delete_resource(resource)
-    print("cleanup complete, removing manifest...")
     delete_resource(MANIFEST)
+    cprint("cleanup complete!", color="green")
 
 
 def init_repo():
@@ -48,7 +53,7 @@ def init_repo():
         overwrite=False
     )
     cprint("pre-commit installed.", "green")
-    run_namespace = Namespace(
+    pre_commit_args = Namespace(
         all_files=True,
         files=[],
         hook_stage='commit',
@@ -62,10 +67,11 @@ def init_repo():
         show_diff_on_failure=False,
         is_squash_merge=False,
     )
+    subprocess.run(["poetry", "install", "-n"])
     pre_commit.run(
         config_file=pre_commit_constants.CONFIG_FILE,
         store=store,
-        args=run_namespace
+        args=pre_commit_args
     )
     repo.index.add_all()
     repo.index.write()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/.dockerignore b/fastapi_template/template/{{cookiecutter.project_name}}/.dockerignore
new file mode 100644
index 0000000..741c3d3
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/.dockerignore
@@ -0,0 +1,143 @@
+### Python template
+
+deploy/
+.idea/
+.vscode/
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+#   For a library or package, you might want to ignore these files since the code is
+#   intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/.editorconfig b/fastapi_template/template/{{cookiecutter.project_name}}/.editorconfig
new file mode 100644
index 0000000..eb831fe
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/.editorconfig
@@ -0,0 +1,31 @@
+root = true
+
+[*]
+tab_width = 4
+end_of_line = lf
+max_line_length = 88
+ij_visual_guides = 88
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{js,py,html}]
+charset = utf-8
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.{yml,yaml}]
+indent_style = space
+indent_size = 2
+
+[Makefile]
+indent_style = tab
+
+[.flake8]
+indent_style = space
+indent_size = 2
+
+[*.py]
+indent_style = space
+indent_size = 4
+ij_python_from_import_parentheses_force_if_multiline = true
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/.env b/fastapi_template/template/{{cookiecutter.project_name}}/.env
new file mode 100644
index 0000000..8d89e98
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/.env
@@ -0,0 +1 @@
+{{cookiecutter.project_name | upper}}_RELOAD=True
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/.flake8 b/fastapi_template/template/{{cookiecutter.project_name}}/.flake8
new file mode 100644
index 0000000..6572ef1
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/.flake8
@@ -0,0 +1,89 @@
+[flake8]
+max-complexity = 6
+inline-quotes = double
+max-line-length = 88
+extend-ignore = E203
+docstring_style=sphinx
+
+ignore =
+  ; Found `f` string
+  WPS305,
+  ; Missing docstring in public module
+  D100,
+  ; Missing docstring in magic method
+  D105,
+  ; Missing docstring in __init__
+  D107,
+  ; Found class without a base class
+  WPS306,
+  ; Missing docstring in public nested class
+  D106,
+  ; First line should be in imperative mood
+  D401,
+  ; Found `__init__.py` module with logic
+  WPS412,
+  ; Found implicit string concatenation
+  WPS326,
+  ; Found string constant over-use
+  WPS226,
+  ; Found upper-case constant in a class
+  WPS115,
+  ; Found nested function
+  WPS430,
+  ; Found using `@staticmethod`
+  WPS602,
+  ; Found method without arguments
+  WPS605,
+  ; Found overused expression
+  WPS204,
+  ; Found too many module members
+  WPS202,
+  ; Found too high module cognitive complexity
+  WPS232,
+  ; line break before binary operator
+  W503,
+  ; Found module with too many imports
+  WPS201,
+  ; Found vague import that may cause confusion: X
+  WPS347,
+  ; Inline strong start-string without end-string.
+  RST210,
+  ; Found nested class
+  WPS431,
+  ; Found wrong module name
+  WPS100,
+  ; Found too many methods
+  WPS214,
+  ; Found too long ``try`` body
+  WPS229,
+  ; Found unpythonic getter or setter
+  WPS615,
+  ; Found a line that starts with a dot
+  WPS348,
+  ; Found complex default value (for dependency injection)
+  WPS404,
+  ;  not perform function calls in argument defaults (for dependency injection)
+  B008,
+
+  ; all init files
+  __init__.py:
+  ; ignore not used imports
+  F401,
+  ; ignore import with wildcard
+  F403,
+  ; Found wrong metadata variable
+  WPS410,
+
+per-file-ignores =
+  ; all tests
+  test_*.py,tests.py,tests_*.py,*/tests/*:
+  ; Use of assert detected
+  S101,
+  ; Found outer scope names shadowing
+  WPS442,
+
+exclude =
+  ./.git,
+  ./venv,
+  migrations,
+  ./var,
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/.gitignore b/fastapi_template/template/{{cookiecutter.project_name}}/.gitignore
index 334abdf..ded1f6c 100644
--- a/fastapi_template/template/{{cookiecutter.project_name}}/.gitignore
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/.gitignore
@@ -1,5 +1,7 @@
-.idea/
+### Python template
 
+.idea/
+.vscode/
 # Byte-compiled / optimized / DLL files
 __pycache__/
 *.py[cod]
@@ -107,7 +109,7 @@ celerybeat.pid
 *.sage.py
 
 # Environments
-envs/.env
+.env
 .venv
 env/
 venv/
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/.gitlab-ci.yml b/fastapi_template/template/{{cookiecutter.project_name}}/.gitlab-ci.yml
index 07c268b..87f801c 100644
--- a/fastapi_template/template/{{cookiecutter.project_name}}/.gitlab-ci.yml
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/.gitlab-ci.yml
@@ -1,33 +1,53 @@
-variables:
-  GIT_STRATEGY: clone
-
 stages:
-  - test
-  - deploy
-
-.install-deps-template: &install-deps
-  before_script:
-    - echo "Installing deps"
-    - python3 -m pip install poetry
-    - poetry install -vv
+  - "test"
 
-run_tests:
-  <<: *install-deps
+.test-template:
   stage: test
+  image: python:3.9.6-slim-buster
   tags:
-    - testing
+    - kubernetes-runner
+    - docker-runner
+  except:
+    - tags
+  before_script:
+    - pip install poetry==1.1.7
+    - poetry config virtualenvs.create false
+    - poetry install
+
+flake8:
+  extends:
+    - .test-template
   script:
-    - poetry run pre-commit run --all-files
-    - poetry run dotenv -f envs/test.env run pytest -vv --cov=src
+    - flake8 --count .
 
-deploy_app:
-  <<: *install-deps
-  stage: deploy
-  only: ['master']
-  tags:
-    - production
+pytest:
+  extends:
+    - .test-template
+  {%- if cookiecutter.db_info.name != "none" %}
+  {%- if cookiecutter.db_info.name != "mysql" %}
+  services:
+    - name: {{ cookiecutter.db_info.image }}
+  {%- endif %}
+  variables:
+    {%- if cookiecutter.db_info.name == "postgresql" %}
+    {{ cookiecutter.project_name | upper }}_DB_HOST: localhost
+    POSTGRES_PASSWORD: {{ cookiecutter.project_name }}
+    POSTGRES_USER: {{ cookiecutter.project_name }}
+    POSTGRES_DB: {{ cookiecutter.project_name }}
+    {%- endif %}
+    {%- if cookiecutter.db_info.name == "mysql" %}
+    {{ cookiecutter.project_name | upper }}_DB_HOST: localhost
+    MYSQL_PASSWORD: {{ cookiecutter.project_name }}
+    MYSQL_USER: {{ cookiecutter.project_name }}
+    MYSQL_DB: {{ cookiecutter.project_name }}
+    {%- endif %}
+  {%- endif %}
+  script:
+    - pytest -vv --cov="{{cookiecutter.project_name}}" .
+
+black:
+  extends:
+    - .test-template
   script:
-    - poetry run dotenv -f "${APP_ENV_FILE}" run alembic upgrade head;
-    - sh deploy.sh
-  environment:
-    name: production
\ No newline at end of file
+    - black --check .
+
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/.pre-commit-config.yaml b/fastapi_template/template/{{cookiecutter.project_name}}/.pre-commit-config.yaml
index 2ed6b76..294b534 100644
--- a/fastapi_template/template/{{cookiecutter.project_name}}/.pre-commit-config.yaml
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/.pre-commit-config.yaml
@@ -6,54 +6,58 @@ repos:
     hooks:
       - id: check-ast
       - id: trailing-whitespace
+      - id: check-toml
+      - id: end-of-file-fixer
 
-  - repo: https://github.com/psf/black
-    rev: 20.8b0
+  - repo: https://github.com/asottile/add-trailing-comma
+    rev: v2.1.0
     hooks:
-      - id: black
+    -   id: add-trailing-comma
 
-  - repo: https://gitlab.com/PyCQA/flake8.git
-    rev: 3.8.3
+  - repo: local
     hooks:
+      - id: black
+        name: Format with Black
+        entry: poetry run black
+        language: system
+        types: [python]
+
+      - id: autoflake
+        name: autoflake
+        entry: poetry run autoflake
+        language: system
+        types: [ python ]
+        args: [ --in-place, --remove-all-unused-imports, --remove-duplicate-keys ]
+
+      - id: isort
+        name: isort
+        entry: poetry run isort
+        language: system
+        types: [ python ]
+
       - id: flake8
-        args:
-          - "--max-line-length=88"
-          - "--extend-ignore=E203"
-        exclude: >
-          (?x)^(
-              migrations/.+
-          )$
-
-  - repo: https://github.com/pre-commit/mirrors-mypy
-    rev: v0.782
-    hooks:
+        name: Check with Flake8
+        entry: poetry run flake8
+        language: system
+        pass_filenames: false
+        types: [ python ]
+        args: [--count, .]
+
       - id: mypy
-        args:
-          - "--strict"
-          - "--ignore-missing-imports"
-          - "--allow-subclassing-any"
-          - "--allow-untyped-calls"
-          - "--pretty"
-          - "--show-error-codes"
-          - "--no-warn-return-any"
-          - "--implicit-reexport"
-          - "--allow-untyped-decorators"
-        exclude: >
-          (?x)^(
-              migrations/.+
-          )$
-
-  - repo: https://github.com/timothycrosley/isort
-    rev: 5.4.2
-    hooks:
-      - id: isort
-        args:
-          - "-l=88"
-          - "--multi-line=3"
-          - "--tc"
-          - "--up"
-          - "--force-grid-wrap=0"
-        exclude: >
-          (?x)^(
-              migrations/.+
-          )$
+        name: Validate types with MyPy
+        entry: poetry run mypy
+        language: system
+        types: [ python ]
+
+      - id: yesqa
+        name: Remove usless noqa
+        entry: poetry run yesqa
+        language: system
+        types: [ python ]
+
+      - id: pytest
+        name: pytest
+        entry: poetry run pytest
+        language: system
+        pass_filenames: false
+        types: [ python ]
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/Dockerfile b/fastapi_template/template/{{cookiecutter.project_name}}/Dockerfile
deleted file mode 100644
index 4631950..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/Dockerfile
+++ /dev/null
@@ -1,105 +0,0 @@
-# `python-base` sets up all our shared environment variables
-FROM python:3.8.5-slim as python-base
-
-    # python
-ENV PYTHONUNBUFFERED=1 \
-    # prevents python creating .pyc files
-    PYTHONDONTWRITEBYTECODE=1 \
-    \
-    # pip
-    PIP_NO_CACHE_DIR=off \
-    PIP_DISABLE_PIP_VERSION_CHECK=on \
-    PIP_DEFAULT_TIMEOUT=100 \
-    \
-    # poetry
-    # https://python-poetry.org/docs/configuration/#using-environment-variables
-    POETRY_VERSION=1.0.10 \
-    # make poetry install to this location
-    POETRY_HOME="/opt/poetry" \
-    # make poetry create the virtual environment in the project's root
-    # it gets named `.venv`
-    POETRY_VIRTUALENVS_IN_PROJECT=true \
-    # do not ask any interactive question
-    POETRY_NO_INTERACTION=1 \
-    \
-    # paths
-    # this is where our requirements + virtual environment will live
-    PYSETUP_PATH="/opt/pysetup" \
-    VENV_PATH="/opt/pysetup/.venv"
-
-RUN apt-get update \
-    && apt-get install --no-install-recommends -y \
-        # deps for installing poetry
-        postgresql=11+200+deb10u4 \
-        libpq-dev=11.9-0+deb10u1 \
-    && apt-get clean \
-    && rm -rf /var/lib/apt/lists/*
-
-# prepend poetry and venv to path
-ENV PATH="$POETRY_HOME/bin:$VENV_PATH/bin:$PATH"
-
-
-# `builder-base` stage is used to build deps + create our virtual environment
-FROM python-base as builder-base
-RUN apt-get update \
-    && apt-get install --no-install-recommends -y \
-        # deps for installing poetry
-        curl=7.64.0-4+deb10u1 \
-        # deps for building python deps
-        build-essential=12.6 \
-    && apt-get clean \
-    && rm -rf /var/lib/apt/lists/*
-
-# This is DL3008
-SHELL ["/bin/bash", "-o", "pipefail", "-c"]
-# install poetry - respects $POETRY_VERSION & $POETRY_HOME
-RUN curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python
-
-# copy project requirement files here to ensure they will be cached.
-WORKDIR $PYSETUP_PATH
-COPY poetry.lock* pyproject.toml ./
-
-# install runtime deps - uses $POETRY_VIRTUALENVS_IN_PROJECT internally
-RUN poetry install --no-dev
-
-# `development` image is used during development / testing
-FROM python-base as development
-ENV FASTAPI_ENV=development
-WORKDIR $PYSETUP_PATH
-
-# copy in our built poetry + venv
-COPY --from=builder-base $POETRY_HOME $POETRY_HOME
-COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
-
-# quicker install as runtime deps are already installed
-RUN poetry install
-
-VOLUME /app
-# will become mountpoint of our code
-WORKDIR /app
-
-EXPOSE 8000
-ENV LOG_LEVEL=DEBUG
-
-CMD [ "uvicorn", "--reload", "--access-log", "--log-level", "debug", "--host", "0.0.0.0", "--port", "8000", "src.server:app" ]
-
-{% if cookiecutter.add_scheduler == "True" -%}
-FROM python-base as scheduler
-WORKDIR $PYSETUP_PATH
-
-# copy in our built poetry + venv
-COPY --from=builder-base $POETRY_HOME $POETRY_HOME
-COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
-
-VOLUME /app
-WORKDIR /app
-
-CMD ["python", "scheduler.py"]
-{% endif %}
-# `production` image used for runtime
-FROM python-base as production
-ENV FASTAPI_ENV=production
-COPY --from=builder-base $PYSETUP_PATH $PYSETUP_PATH
-COPY . /app/
-WORKDIR /app
-CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "src.server:app"]
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/README.md b/fastapi_template/template/{{cookiecutter.project_name}}/README.md
index 7d9052d..621d8f8 100644
--- a/fastapi_template/template/{{cookiecutter.project_name}}/README.md
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/README.md
@@ -1,69 +1,7 @@
 # {{cookiecutter.project_name}}
 
-{{cookiecutter.project_description}}
+Start a project with:
 
-## For developers
-
-To run project locally you can just type `docker-compose up`
-
-### Needed environment variables for CI\CD
-* APP_ENV_FILE - path to .env file on server
-
-## How to run the application
-
-At first, you need to install all dependencies with poetry
-
-```bash
-poetry install
-```
-
-For tests you need to set all needed environment variables.
-All needed variables shown in `envs/.env.example`.
-
-If all new environment parameters are stored in .env file, then you better to start testing/application using `dotenv` program, or set  `EnvironmentFile` in your systemd-module. 
-
-### Applying migrations and startup
-Before running the application don't forget to apply pending migrations. To do so run the following command in the application directory:
-```bash
-dotenv -f ".env.file" run alembic upgrade head;
-```
-
-To test and start the application with `dotenv` run following commands: 
-```bash
-# Testing
-dotenv -f envs/test.env run pytest
-# Startup
-dotenv -f envs/.env run uvicorn --access-log --log-level debug --host 0.0.0.0 --port 8100 src.server:app
-```
-
-If all environment variables already set: 
-```bash
-# Testing
-pytest
-# Startup
-uvicorn --access-log --log-level debug --host 0.0.0.0 --port 8100 src.server:app
 ```
-
-Example of configured systemd module for application shown in [systemd/{{cookiecutter.project_name}}_service.service](systemd/{{ cookiecutter.project_name }}_service.service) file.
-
-
-## Task scheduler startup
-Task scheduler is a script that runs background tasks periodically.
-You can read more about scheduler at [aioschedule docs](https://pypi.org/project/aioschedule/) 
-
-```bash
-# Command to run scheduler script with existing environment variables:
-python scheduler.py
- 
-# Command to run sceduler with .env file: 
-dotenv -f envs/.env run python scheduler.py
+docker-compose -f deploy/docker-compose.yml --project-directory . up
 ```
-
-Example of configured systemd module for scheduler shown in [systemd/{{cookiecutter.project_name}}_scheduler.service](systemd/{{ cookiecutter.project_name }}_scheduler.service)
-
-## About systemd files
-
-To use systemd files on different machine you may need to change following parameters:
-* EnvironmentFile - absolute path to .env file to use during execution;
-* WorkingDirectory - absolute path to project directory on your machine or server;
-* ExecStart - command to start the application.
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/alembic.ini b/fastapi_template/template/{{cookiecutter.project_name}}/alembic.ini
index 97a52b1..588ed01 100644
--- a/fastapi_template/template/{{cookiecutter.project_name}}/alembic.ini
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/alembic.ini
@@ -1,52 +1,22 @@
-# A generic, single database configuration.
-
 [alembic]
-# path to migration scripts
-script_location = migrations
-
-# template used to generate migration files
-# file_template = %%(rev)s_%%(slug)s
-
-# timezone to use when rendering the date
-# within the migration file as well as the filename.
-# string value is passed to dateutil.tz.gettz()
-# leave blank for localtime
-# timezone =
-
-# max length of characters to apply to the
-# "slug" field
+script_location = {{cookiecutter.project_name}}/db/migrations
+file_template = %%(year)d-%%(month).2d-%%(day).2d-%%(hour).2d-%%(minute).2d_%%(rev)s
+prepend_sys_path = .
+output_encoding = utf-8
 # truncate_slug_length = 40
 
-# set to 'true' to run the environment during
-# the 'revision' command, regardless of autogenerate
-# revision_environment = false
-
-# set to 'true' to allow .pyc and .pyo files without
-# a source .py file to be detected as revisions in the
-# versions/ directory
-# sourceless = false
-
-# version location specification; this defaults
-# to src/alembic/versions.  When using multiple version
-# directories, initial revisions must be specified with --version-path
-# version_locations = %(here)s/bar %(here)s/bat src/alembic/versions
-
-# the output encoding used when revision files
-# are written from script.py.mako
-# output_encoding = utf-8
 
+[post_write_hooks]
+hooks = black,autoflake,isort
 
+black.type = console_scripts
+black.entrypoint = black
 
-[post_write_hooks]
-# post_write_hooks defines scripts or Python functions that are run
-# on newly generated revision scripts.  See the documentation for further
-# detail and examples
+autoflake.type = console_scripts
+autoflake.entrypoint = autoflake
 
-# format using "black" - use the console_scripts runner, against the "black" entrypoint
-# hooks=black
-# black.type=console_scripts
-# black.entrypoint=black
-# black.options=-l 79
+isort.type = console_scripts
+isort.entrypoint = isort
 
 # Logging configuration
 [loggers]
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.json b/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.json
index ce4d22d..3f5ca14 100644
--- a/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.json
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/conditional_files.json
@@ -1,44 +1,39 @@
 {
-  "features": [
-    {
-      "name": "Redis support",
-      "enabled": "{{cookiecutter.add_redis|lower}}",
-      "resources": [
-        "src/services/redis.py",
-        "src/api/redis_api"
-      ]
-    },
-    {
-      "name": "Task scheduler",
-      "enabled": "{{cookiecutter.add_scheduler|lower}}",
-      "resources": [
-        "scheduler.py",
-        "systemd/{{ cookiecutter.project_name }}_scheduler.service"
-      ]
-    },
-    {
-      "name": "Systemd support",
-      "enabled": "{{cookiecutter.add_systemd|lower}}",
-      "resources": [
-        "systemd"
-      ]
-    },
-    {
-      "name": "Dummy DB model",
-      "enabled": "{{cookiecutter.add_dummy_model|lower}}",
-      "resources": [
-        "src/models/dummy_db_model.py",
-        "migrations/versions/7ae297ab5ac1_created_dummy_model.py",
-        "tests/dummy_db_test.py",
-        "src/api/dummy_db"
-      ]
-    },
-    {
-      "name": "Elastic search",
-      "enabled": "{{cookiecutter.add_elastic_search|lower}}",
-      "resources": [
-        "src/services/elastic"
-      ]
-    }
-  ]
-}
\ No newline at end of file
+  "Redis": {
+    "enabled": "{{cookiecutter.enable_redis}}",
+    "resources": [
+      "{{cookiecutter.project_name}}/web/api/redis",
+      "{{cookiecutter.project_name}}/services/redis",
+      "deploy/kube/redis.yml"
+    ]
+  },
+  "Kubernetes": {
+    "enabled": "{{cookiecutter.enable_kube}}",
+    "resources": [
+      "deploy/kube"
+    ]
+  },
+  "Database support": {
+    "enabled": "{{cookiecutter.db_info.name != 'none'}}",
+    "resources": [
+      "{{cookiecutter.project_name}}/web/api/dummy",
+      "{{cookiecutter.project_name}}/db",
+      "{{cookiecutter.project_name}}/conftest.py",
+      "deploy/kube/db.yml",
+      "alembic.ini"
+    ]
+  },
+  "Alembic": {
+    "enabled": "{{cookiecutter.enable_alembic}}",
+    "resources": [
+      "alembic.ini",
+      "{{cookiecutter.project_name}}/db/migrations"
+    ]
+  },
+  "CI support": {
+    "enabled": "{{cookiecutter.enable_ci}}",
+    "resources": [
+      ".gitlab-ci.yml"
+    ]
+  }
+}
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/deploy.sh b/fastapi_template/template/{{cookiecutter.project_name}}/deploy.sh
deleted file mode 100644
index baf38a9..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/deploy.sh
+++ /dev/null
@@ -1,14 +0,0 @@
-echo "Your deploy steps must run here"
-
-{% if cookiecutter.add_systemd == "True" -%}
-# Function to replace stub values in service files
-function replace_stubs() {
-  sed -i "s#WorkingDirectory=.*#WorkingDirectory=${APP_DIR:?}#" "$1"
-  sed -i "s#EnvironmentFile=ENV_FILE*#EnvironmentFile=${ENV_FILE_PATH:?}#" "$1"
-}
-
-replace_stubs "./systemd/{{ cookiecutter.project_name }}_service.service"
-{% if cookiecutter.add_scheduler == "True" -%}
-replace_stubs "./systemd/{{ cookiecutter.project_name }}_scheduler.service"
-{% endif %}
-{% endif %}
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/Dockerfile b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/Dockerfile
new file mode 100644
index 0000000..b40f5eb
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/Dockerfile
@@ -0,0 +1,32 @@
+FROM python:3.9.6-slim-buster
+
+RUN useradd -m {{cookiecutter.project_name}}
+
+USER {{cookiecutter.project_name}}
+
+ENV PATH="${PATH}:/home/{{cookiecutter.project_name}}/.poetry/bin:/home/{{cookiecutter.project_name}}/.local/bin"
+
+RUN pip install poetry==1.1.7
+
+# Installing requirements
+RUN poetry config virtualenvs.create false
+
+COPY pyproject.toml poetry.lock /home/{{cookiecutter.project_name}}/app/
+WORKDIR /home/{{cookiecutter.project_name}}/app/
+
+RUN poetry install --no-dev
+
+# Copying actuall application
+COPY . /home/{{cookiecutter.project_name}}/app/src/
+WORKDIR /home/{{cookiecutter.project_name}}/app/src/
+RUN pip install --use-feature=in-tree-build  .
+
+WORKDIR /home/{{cookiecutter.project_name}}/app
+
+USER root
+RUN rm -rf /home/{{cookiecutter.project_name}}/app/src
+RUN chown -R {{cookiecutter.project_name}} /home/{{cookiecutter.project_name}}
+RUN chmod -R 700 /home/{{cookiecutter.project_name}}
+USER {{cookiecutter.project_name}}
+
+CMD python -m {{cookiecutter.project_name}}
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.yml b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.yml
new file mode 100644
index 0000000..3a0be7c
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/docker-compose.yml
@@ -0,0 +1,45 @@
+version: '3.7'
+
+services:
+  api:
+    build:
+      context: .
+      dockerfile: ./deploy/Dockerfile
+    env_file:
+      - .env
+    environment:
+      - {{cookiecutter.project_name | upper }}_VAR=test # stub
+      {%- if cookiecutter.db_info.name == "postgresql" %}
+      - {{cookiecutter.project_name | upper }}_DB_PORT=5432
+      {%- endif %}
+      {%- if cookiecutter.db_info.name == "mysql" %}
+      - {{cookiecutter.project_name | upper }}_DB_PORT=3306
+      {%- endif %}
+
+  {%- if cookiecutter.db_info.name == "postgresql" %}
+  db:
+    image: postgres:13.4-buster
+    hostname: {{cookiecutter.project_name}}-db
+    environment:
+      - POSTGRES_PASSWORD={{cookiecutter.project_name}}
+      - POSTGRES_USER={{cookiecutter.project_name}}
+      - POSTGRES_DB={{cookiecutter.project_name}}
+  {%- endif %}
+
+  {%- if cookiecutter.db_info.name == "mysql" %}
+  db:
+    image: bitnami/mysql:8.0.26
+    hostname: {{cookiecutter.project_name}}-db
+    environment:
+      - MYSQL_PASSWORD={{cookiecutter.project_name}}
+      - MYSQL_USER={{cookiecutter.project_name}}
+      - MYSQL_DATABASE={{cookiecutter.project_name}}
+  {%- endif %}
+
+  {%- if cookiecutter.enable_redis == "True" %}
+  redis:
+    image: bitnami/redis:6.2.5
+    hostname: {{cookiecutter.project_name}}-redis
+    environment:
+      - ALLOW_EMPTY_PASSWORD=yes
+  {%- endif %}
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/app.yml b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/app.yml
new file mode 100644
index 0000000..8d9330c
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/app.yml
@@ -0,0 +1,57 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{cookiecutter.project_name}}
+spec:
+  selector:
+    matchLabels:
+      app: {{cookiecutter.project_name}}
+  template:
+    metadata:
+      labels:
+        app: {{cookiecutter.project_name}}
+    spec:
+      containers:
+        - name: {{cookiecutter.project_name}}
+          image: {{cookiecutter.project_name}}:latest
+          imagePullPolicy: Always
+          envFrom:
+            - configMapRef:
+                name: {{cookiecutter.project_name}}-env
+          resources:
+            limits:
+              memory: "300Mi"
+              cpu: "200m"
+          ports:
+            - containerPort: 8000
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: {{cookiecutter.project_name}}
+spec:
+  selector:
+    app: {{cookiecutter.project_name}}
+  ports:
+    - port: 80
+      targetPort: 8000
+---
+apiVersion: networking.k8s.io/v1
+kind: Ingress
+metadata:
+  name: {{cookiecutter.project_name}}
+  labels:
+    name: {{cookiecutter.project_name}}
+spec:
+  rules:
+    - host: {{cookiecutter.project_name}}.local
+      http:
+        paths:
+          - pathType: Prefix
+            path: "/"
+            backend:
+              service:
+                name: {{cookiecutter.project_name}}
+                port:
+                  number: 80
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/db.yml b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/db.yml
new file mode 100644
index 0000000..5fe6ed1
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/db.yml
@@ -0,0 +1,33 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{cookiecutter.project_name}}-db
+spec:
+  selector:
+    matchLabels:
+      app: {{cookiecutter.project_name}}-db
+  template:
+    metadata:
+      labels:
+        app: {{cookiecutter.project_name}}-db
+    spec:
+      containers:
+        - name: database
+          image: {{cookiecutter.db_info.image}}
+          resources:
+            limits:
+              memory: "300Mi"
+              cpu: "200m"
+          ports:
+            - containerPort: 5432
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: "{{cookiecutter.project_name}}-db"
+spec:
+  selector:
+    app: {{cookiecutter.project_name}}-db
+  ports:
+    - port: {{cookiecutter.db_info.port}}
+      targetPort: {{cookiecutter.db_info.port}}
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/redis.yml b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/redis.yml
new file mode 100644
index 0000000..4e1e304
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/deploy/kube/redis.yml
@@ -0,0 +1,33 @@
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: {{cookiecutter.project_name}}-redis
+spec:
+  selector:
+    matchLabels:
+      app: {{cookiecutter.project_name}}-redis
+  template:
+    metadata:
+      labels:
+        app: {{cookiecutter.project_name}}-redis
+    spec:
+      containers:
+        - name: redis
+          image: bitnami/redis:6.2.5
+          resources:
+            limits:
+              memory: "300Mi"
+              cpu: "200m"
+          ports:
+            - containerPort: 6379
+---
+apiVersion: v1
+kind: Service
+metadata:
+  name: "{{cookiecutter.project_name}}-redis"
+spec:
+  selector:
+    app: {{cookiecutter.project_name}}-redis
+  ports:
+    - port: 6379
+      targetPort: 6379
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/docker-compose.prod.yml b/fastapi_template/template/{{cookiecutter.project_name}}/docker-compose.prod.yml
deleted file mode 100644
index 54c42e8..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/docker-compose.prod.yml
+++ /dev/null
@@ -1,82 +0,0 @@
-# Local docker-compose configuration
-
-version: '3.7'
-
-services:
-  back:
-    container_name: {{ cookiecutter.project_name }}_backend
-    build:
-      context: .
-      target: production
-      labels:
-        {{ cookiecutter.project_name }}.description: {{ cookiecutter.project_description }}
-    env_file:
-      - envs/.env
-    depends_on:
-      - db
-    networks:
-      - {{ cookiecutter.project_name }}_network
-
-  db:
-    container_name: {{ cookiecutter.project_name }}_db
-    image: postgres:12.4
-    volumes:
-      - db_data:/var/lib/postgresql/data
-    networks:
-      - {{ cookiecutter.project_name }}_network
-    env_file:
-      - envs/.env
-
-  {% if cookiecutter.add_redis == "True" -%}
-  redis:
-    container_name: {{ cookiecutter.project_name }}_redis
-    image: bitnami/redis:6.0.7
-    volumes:
-      - redis_data:/bitnami/redis/data
-    env_file:
-      - envs/.env
-    networks:
-      - {{ cookiecutter.project_name }}_network
-  {% endif %}
-
-  {% if cookiecutter.add_scheduler == "True" -%}
-  scheduler:
-    container_name: {{ cookiecutter.project_name }}_scheduler
-    build:
-      context: .
-      target: scheduler
-      labels:
-        scheduler.description: "{{ cookiecutter.project_name }} scheduler image"
-    env_file:
-      - envs/.env
-    volumes:
-    - ./:/app
-    depends_on:
-      - db
-    networks:
-      - {{ cookiecutter.project_name }}_network
-  {% endif %}
-
-  {% if cookiecutter.add_elastic_search == "True" -%}
-  es:
-    restart: always
-    container_name: {{ cookiecutter.project_name }}_es_dev
-    image: elasticsearch:7.3.0
-    volumes:
-      - es_data_dev:/usr/share/elasticsearch/data
-    environment:
-      - discovery.type=single-node
-      - bootstrap.memory_lock=true
-      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
-    networks:
-      - {{ cookiecutter.project_name }}_network_dev
-  {% endif %}
-
-volumes:
-  db_data:
-  redis_data:
-  es_data_dev:
-
-networks:
-  {{ cookiecutter.project_name }}_network:
-    name: {{ cookiecutter.project_name }}_network
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/docker-compose.yml b/fastapi_template/template/{{cookiecutter.project_name}}/docker-compose.yml
deleted file mode 100644
index 3fe9d8c..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/docker-compose.yml
+++ /dev/null
@@ -1,90 +0,0 @@
-# Local docker-compose configuration
-
-version: '3.7'
-
-services:
-  back: &back
-    container_name: {{ cookiecutter.project_name }}_backend_dev
-    build:
-      context: .
-      target: development
-      labels:
-        {{ cookiecutter.project_name }}.description: {{ cookiecutter.project_description }}
-    env_file:
-      - envs/.env
-    ports:
-      - {{ cookiecutter.default_port }}:8000
-    volumes:
-      - ./:/app
-    depends_on:
-      - db
-    networks:
-      - {{ cookiecutter.project_name }}_network_dev
-
-  db:
-    container_name: {{ cookiecutter.project_name }}_db_dev
-    image: postgres:12.4
-    volumes:
-      - db_data_dev:/var/lib/postgresql/data
-    ports:
-      - 5432:5432
-    networks:
-      - {{ cookiecutter.project_name }}_network_dev
-    env_file:
-      - envs/.env
-
-  {% if cookiecutter.add_redis == "True" -%}
-  redis:
-    container_name: {{ cookiecutter.project_name }}_redis_dev
-    image: bitnami/redis:6.0.7
-    volumes:
-      - redis_data_dev:/bitnami/redis/data
-    env_file:
-      - envs/.env
-    ports:
-      - 6379:6379
-    networks:
-      - {{ cookiecutter.project_name }}_network_dev
-  {% endif %}
-
-  {% if cookiecutter.add_scheduler == "True" -%}
-  scheduler:
-    container_name: {{ cookiecutter.project_name }}_scheduler_dev
-    build:
-      context: .
-      target: scheduler
-      labels:
-        scheduler.description: "{{ cookiecutter.project_name }} scheduler image"
-    env_file:
-      - envs/.env
-    volumes:
-    - ./:/app
-    depends_on:
-      - db
-    networks:
-      - {{ cookiecutter.project_name }}_network_dev
-  {% endif %}
-
-  {% if cookiecutter.add_elastic_search == "True" -%}
-  es:
-    restart: always
-    container_name: {{ cookiecutter.project_name }}_es_dev
-    image: elasticsearch:7.3.0
-    volumes:
-      - es_data_dev:/usr/share/elasticsearch/data
-    environment:
-      - discovery.type=single-node
-      - bootstrap.memory_lock=true
-      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
-    networks:
-      - {{ cookiecutter.project_name }}_network_dev
-  {% endif %}
-
-volumes:
-  db_data_dev:
-  redis_data_dev:
-  es_data_dev:
-
-networks:
-  {{ cookiecutter.project_name }}_network_dev:
-    name: {{ cookiecutter.project_name }}_network_dev
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/envs/.env b/fastapi_template/template/{{cookiecutter.project_name}}/envs/.env
deleted file mode 100644
index 4c2e415..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/envs/.env
+++ /dev/null
@@ -1,17 +0,0 @@
-LOG_LEVEL=Debug
-POSTGRES_HOST=db
-POSTGRES_PORT=5432
-POSTGRES_DB={{cookiecutter.project_name}}_db
-POSTGRES_USER={{cookiecutter.project_name}}
-POSTGRES_PASSWORD={{cookiecutter.postgres_password}}
-{% if cookiecutter.add_redis == "True" -%}
-REDIS_PASSWORD={{cookiecutter.redis_password}}
-REDIS_HOST=redis
-REDIS_PORT=6379
-{% endif %}
-{% if cookiecutter.add_scheduler == "True" -%}
-SCHEDULE_TIMER=20
-{% endif %}
-{% if cookiecutter.add_elastic_search == "True" -%}
-ELASTIC_HOST=http://es:9200
-{% endif %}
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/envs/example.env b/fastapi_template/template/{{cookiecutter.project_name}}/envs/example.env
deleted file mode 100644
index f4ffb55..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/envs/example.env
+++ /dev/null
@@ -1,18 +0,0 @@
-POSTGRES_HOST=db
-POSTGRES_PORT=5432
-POSTGRES_DB=secret_db
-POSTGRES_USER=secret_user
-POSTGRES_PASSWORD=SomeSecretRedisPassword
-LOG_LEVEL=Debug
-{% if cookiecutter.add_redis == "True" -%}
-REDIS_PASSWORD=SomeSecretRedisPassword
-REDIS_HOST=redis
-REDIS_PORT=6379
-{% endif %}
-HTTPBIN_HOST=https://httpbin.org/
-{% if cookiecutter.add_scheduler == "True" -%}
-SCHEDULE_TIMER=20
-{% endif %}
-{% if cookiecutter.add_elastic_search == "True" -%}
-ELASTIC_HOST=http://es:9200
-{% endif %}
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/envs/test.env b/fastapi_template/template/{{cookiecutter.project_name}}/envs/test.env
deleted file mode 100644
index 96a9cb7..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/envs/test.env
+++ /dev/null
@@ -1,18 +0,0 @@
-POSTGRES_HOST=localhost
-POSTGRES_PORT=5432
-POSTGRES_DB={{cookiecutter.project_name}}_db_test
-POSTGRES_USER={{cookiecutter.project_name}}
-POSTGRES_PASSWORD={{cookiecutter.postgres_password}}
-DB_ECHO=True
-{% if cookiecutter.add_redis == "True" -%}
-REDIS_PASSWORD={{cookiecutter.redis_password}}
-REDIS_HOST=localhost
-REDIS_PORT=6379
-{% endif %}
-HTTPBIN_HOST=https://httpbin.org/
-{% if cookiecutter.add_scheduler == "True" -%}
-SCHEDULE_TIMER=20
-{% endif %}
-{% if cookiecutter.add_elastic_search == "True" -%}
-ELASTIC_HOST=http://es:9200
-{% endif %}
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/main.py b/fastapi_template/template/{{cookiecutter.project_name}}/main.py
deleted file mode 100644
index 90b6168..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/main.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import argparse
-from typing import Any, Dict, Optional
-
-from fastapi import FastAPI
-from gunicorn.app.base import BaseApplication
-
-from src.server import app
-
-
-class StandaloneApplication(BaseApplication):
-    def __init__(self, application_instance: FastAPI, run_options: Optional[Dict[str, Any]] = None):
-        self.options = run_options or {}
-        self.application = application_instance
-        super().__init__()
-
-    def load_config(self) -> None:
-        config = {
-            key: value
-            for key, value in self.options.items()
-            if key in self.cfg.settings and value is not None
-        }
-        for key, value in config.items():
-            self.cfg.set(key.lower(), value)
-
-    def load(self) -> FastAPI:
-        return self.application
-
-
-def parse_args() -> argparse.Namespace:
-    parser = argparse.ArgumentParser()
-    parser.add_argument("--host", type=str, default="0.0.0.0")
-    parser.add_argument("--port", type=int, default=8000)
-    parser.add_argument("--pid-file", type=str, default="/tmp/{{cookiecutter.project_name}}.pid")
-    return parser.parse_args()
-
-
-if __name__ == "__main__":
-    args = parse_args()
-    options = {
-        "bind": f"{args.host}:{args.port}",
-        "workers": 4,
-        "worker_class": "uvicorn.workers.UvicornWorker",
-        "pidfile": args.pid_file,
-    }
-    StandaloneApplication(app, options).run()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/migrations/env.py b/fastapi_template/template/{{cookiecutter.project_name}}/migrations/env.py
deleted file mode 100644
index 9df59ac..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/migrations/env.py
+++ /dev/null
@@ -1,96 +0,0 @@
-import os
-import sys
-from logging.config import fileConfig
-
-from alembic import context
-from sqlalchemy import create_engine
-from sqlalchemy.engine.url import URL, make_url
-
-sys.path.insert(0, os.path.abspath(""))
-# this is the Alembic Config object, which provides
-# access to the values within the .ini file in use.
-# noqa
-from src.settings import settings
-
-config = context.config
-
-# Interpret the config file for Python logging.
-# This line sets up loggers basically.
-fileConfig(config.config_file_name)
-
-# add your model's MetaData object here
-# for 'autogenerate' support
-# from myapp import mymodel
-# target_metadata = mymodel.Base.metadata
-# noqa
-from src.services.db import db_meta as target_metadata
-from src.models import *  # isort:skip
-
-
-def run_migrations_offline():
-    """Run migrations in 'offline' mode.
-
-    This configures the context with just a URL
-    and not an Engine, though an Engine is acceptable
-    here as well.  By skipping the Engine creation
-    we don't even need a DBAPI to be available.
-
-    Calls to context.execute() here emit the given string to the
-    script output.
-
-    """
-    url = make_url(
-        URL(
-            drivername=settings.db_driver,
-            username=settings.postgres_user,
-            password=settings.postgres_password,
-            host=settings.postgres_host,
-            port=settings.postgres_port,
-            database=settings.postgres_db,
-        )
-    )
-
-    context.configure(
-        url=url,
-        target_metadata=target_metadata,
-        literal_binds=True,
-        dialect_opts={"paramstyle": "named"},
-    )
-
-    with context.begin_transaction():
-        context.run_migrations()
-
-
-def run_migrations_online():
-    """Run migrations in 'online' mode.
-
-    In this scenario we need to create an Engine
-    and associate a connection with the context.
-
-    """
-    url = make_url(
-        URL(
-            drivername=settings.db_driver,
-            username=settings.postgres_user,
-            password=settings.postgres_password,
-            host=settings.postgres_host,
-            port=settings.postgres_port,
-            database=settings.postgres_db,
-        )
-    )
-
-    connectable = create_engine(
-        str(url),
-    )
-
-    with connectable.connect() as connection:
-        context.configure(connection=connection, target_metadata=target_metadata)
-
-        with context.begin_transaction():
-            context.run_migrations()
-
-
-if context.is_offline_mode():
-    run_migrations_offline()
-else:
-    run_migrations_online()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/migrations/versions/7ae297ab5ac1_created_dummy_model.py b/fastapi_template/template/{{cookiecutter.project_name}}/migrations/versions/7ae297ab5ac1_created_dummy_model.py
deleted file mode 100644
index 3b1e426..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/migrations/versions/7ae297ab5ac1_created_dummy_model.py
+++ /dev/null
@@ -1,42 +0,0 @@
-"""Added dummy model
-
-Revision ID: f9c5d2dba936
-Revises:
-Create Date: 2020-10-05 23:56:58.658606
-
-"""
-import sqlalchemy as sa
-from alembic import op
-from sqlalchemy.dialects import postgresql
-
-# revision identifiers, used by Alembic.
-revision = 'f9c5d2dba936'
-down_revision = None
-branch_labels = None
-depends_on = None
-
-
-def upgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.create_table('dummydbmodel',
-                    sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
-                    sa.Column('name', sa.String(), nullable=False),
-                    sa.Column('surname', sa.String(), nullable=False),
-                    {% if cookiecutter.add_elastic_search == "True" -%}sa.Column('tags', sa.String(), nullable=False, default=""),{% endif %}
-                    sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('clock_timestamp()'),
-                              nullable=False),
-                    sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('clock_timestamp()'),
-                              nullable=False),
-                    sa.PrimaryKeyConstraint('id')
-                    )
-    op.create_index(op.f('ix_dummydbmodel_name'), 'dummydbmodel', ['name'], unique=False)
-    op.create_index(op.f('ix_dummydbmodel_surname'), 'dummydbmodel', ['surname'], unique=False)
-    # ### end Alembic commands ###
-
-
-def downgrade():
-    # ### commands auto generated by Alembic - please adjust! ###
-    op.drop_index(op.f('ix_dummydbmodel_surname'), table_name='dummydbmodel')
-    op.drop_index(op.f('ix_dummydbmodel_name'), table_name='dummydbmodel')
-    op.drop_table('dummydbmodel')
-    # ### end Alembic commands ###
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml b/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml
index 811eab5..b93dc40 100644
--- a/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml
@@ -2,39 +2,76 @@
 name = "{{cookiecutter.project_name}}"
 version = "0.1.0"
 description = "{{cookiecutter.project_description}}"
-authors = ["{{cookiecutter.full_name}} <{{cookiecutter.email}}>"]
+authors = [
+
+]
+maintainers = [
+
+]
+readme = "README.md"
 
 [tool.poetry.dependencies]
-python = "^3.8"
-uvicorn = "^0.11.8"
-fastapi = "^0.61.1"
-sqlalchemy = "^1.3.19"
-loguru = "^0.5.2"
-alembic = "^1.4.3"
-httpx = "^0.14.3"
-ujson = "^4.0.1"
-gunicorn = "^20.0.4"
-httptools = "^0.1.1"
-aiopg = "^1.0.0"
-{% if cookiecutter.add_redis == "True" -%}
-aioredis = "^1.3.1"
-{% endif %}
-{% if cookiecutter.add_elastic_search == "True" -%}
-elasticsearch-dsl = "^7.3.0"
-elasticsearch = {extras = ["async"], version = "^7.9.1"}
-{% endif %}
-{% if cookiecutter.add_scheduler == "True" -%}
-aioschedule = "^0.5.2"
-{% endif %}
+python = "^3.9"
+fastapi = "^0.68.0"
+uvicorn = "^0.15.0"
+pydantic = {version = "^1.8.2", extras = ["dotenv"]}
+yarl = "^1.6.3"
+{%- if cookiecutter.db_info.name != "none" %}
+SQLAlchemy = {version = ">=1.4,<1.4.23", extras = ["mypy"]}
+greenlet = "^1.1.1"
+{%- endif %}
+{% if cookiecutter.enable_alembic == "True" %}
+alembic = "^1.6.5"
+{%- endif %}
+{%- if cookiecutter.db_info.name == "postgresql" %}
+asyncpg = {version = "^0.24.0", extras = ["sa"]}
+{%- endif %}
+{%- if cookiecutter.db_info.name == "sqlite" %}
+aiosqlite = "^0.17.0"
+{%- endif %}
+{%- if cookiecutter.db_info.name == "mysql" %}
+aiomysql = "^0.0.21"
+{%- endif %}
+{%- if cookiecutter.enable_redis == "True" %}
+aioredis = {version = "^2.0.0", extras = ["hiredis"]}
+{%- endif %}
 
 [tool.poetry.dev-dependencies]
-pytest = "^6.0.2"
-requests = "^2.24.0"
-pytest-asyncio = "^0.14.0"
-pre-commit = "^2.7.1"
-pytest-cov = "^2.10.1"
-python-dotenv = "^0.14.0"
+pytest = "^6.0"
+flake8 = "^3.9.2"
+mypy = "^0.910"
+isort = "^5.9.3"
+yesqa = "^1.2.3"
+pre-commit = "^2.11.0"
+wemake-python-styleguide = "^0.15.3"
+black = "^21.7b0"
+{%- if cookiecutter.db_info.name != "none" %}
+pytest-async-sqlalchemy = "^0.1.3"
+{%- endif %}
+autoflake = "^1.4"
+{%- if cookiecutter.enable_alembic %}
+pytest-alembic = "^0.3.3"
+{%- endif %}
+pytest-cov = "^2.12.1"
+pytest-asyncio = "^0.15.1"
+
+[tool.isort]
+profile = "black"
+multi_line_output = 3
+
+[tool.mypy]
+strict = true
+ignore_missing_imports = true
+allow_subclassing_any = true
+allow_untyped_calls = true
+pretty = true
+show_error_codes = true
+implicit_reexport = true
+allow_untyped_decorators = true
+{%- if cookiecutter.db_info.name != "none" %}
+plugins = ["sqlalchemy.ext.mypy.plugin"]
+{%- endif %}
 
 [build-system]
-requires = ["poetry>=0.12"]
-build-backend = "poetry.masonry.api"
\ No newline at end of file
+requires = ["poetry-core>=1.0.0"]
+build-backend = "poetry.core.masonry.api"
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/pytest.ini b/fastapi_template/template/{{cookiecutter.project_name}}/pytest.ini
deleted file mode 100644
index 69ecb57..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/pytest.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[pytest]
-minversion = 6.0
-testpaths =
-    tests
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/scheduler.py b/fastapi_template/template/{{cookiecutter.project_name}}/scheduler.py
deleted file mode 100644
index 513db02..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/scheduler.py
+++ /dev/null
@@ -1,19 +0,0 @@
-import asyncio
-
-import aioschedule as schedule
-from loguru import logger
-
-from src.settings import settings
-
-
-async def scheduled_task() -> None:
-    logger.info("I was scheduled to run now")
-
-
-schedule.every(settings.schedule_timer).seconds.do(scheduled_task)
-
-if __name__ == "__main__":
-    logger.debug("Started scheduler")
-    loop = asyncio.get_event_loop()
-    while True:
-        loop.run_until_complete(schedule.run_pending())
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/api/__init__.py
deleted file mode 100644
index f4114f6..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/__init__.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from fastapi import APIRouter
-
-from src.api.httpbin import URL_PREFIX as httpbin_prefix
-from src.api.httpbin import router as httpbin_router
-{% if cookiecutter.add_redis == "True" -%}
-from src.api.redis_api import URL_PREFIX as redis_prefix
-from src.api.redis_api import router as redis_router
-{% endif %}
-{% if cookiecutter.add_dummy_model == "True" -%}
-from src.api.dummy_db import URL_PREFIX as dummy_db_prefix
-from src.api.dummy_db import router as dummy_db_router
-{% endif %}
-
-api_router = APIRouter()
-
-api_router.include_router(
-    router=httpbin_router, prefix=httpbin_prefix, tags=['HTTPBin']
-)
-{% if cookiecutter.add_redis == "True" -%}
-api_router.include_router(
-    router=redis_router, prefix=redis_prefix, tags=["Redis"]
-)
-{% endif %}
-{% if cookiecutter.add_dummy_model == "True" -%}
-api_router.include_router(
-    router=dummy_db_router, prefix=dummy_db_prefix, tags=["Dummy db object"]
-)
-{% endif %}
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/__init__.py
deleted file mode 100644
index df53d3a..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from src.api.dummy_db.routes import URL_PREFIX, router
-
-__all__ = ['router', 'URL_PREFIX']
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/routes.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/routes.py
deleted file mode 100644
index 886ed07..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/routes.py
+++ /dev/null
@@ -1,88 +0,0 @@
-import uuid
-from typing import Optional
-
-from fastapi import APIRouter, Depends
-
-from src.api.dummy_db.schema import (
-    UpdateDummyModel,
-    GetDummyResponse,
-)
-{% if cookiecutter.add_elastic_search == "True" -%}
-from src.api.dummy_db.schema import DummyElasticResponse, ElasticAdd
-from src.services.elastic.schema import ElasticFilterModel
-{%else%}
-from src.api.dummy_db.schema import BaseDummyModel
-{% endif %}
-
-from src.models import DummyDBModel
-from src.services.db.session import Session, db_session
-
-router = APIRouter()
-URL_PREFIX = "/dummy_db_obj"
-
-
-@router.put("/")
-async def create_dummy(dummy_obj: {% if cookiecutter.add_elastic_search == "True" -%}ElasticAdd{% else %}BaseDummyModel{% endif %}, session: Session = Depends(db_session)) -> None:
-    """
-    Add dummy object in database.
-    If you have elastic search feature enabled it will be added in your index.
-    """
-    {% if cookiecutter.add_elastic_search == "True" -%}
-    insert_query = DummyDBModel.create(**dummy_obj.dict()).returning(DummyDBModel.id)
-    model_id = await session.scalar(insert_query)
-    await DummyDBModel.elastic_add(
-        model_id=model_id,
-        **dummy_obj.dict()
-    )
-    {% else %}
-    await session.execute(DummyDBModel.create(**dummy_obj.dict()))
-    {% endif %}
-
-@router.post("/{dummy_id}")
-async def update_dummy_model(
-        dummy_id: uuid.UUID,
-        new_values: UpdateDummyModel,
-        session: Session = Depends(db_session)
-) -> None:
-    await session.execute(DummyDBModel.update(dummy_id, **new_values.dict()))
-    {% if cookiecutter.add_elastic_search == "True" -%}
-    await DummyDBModel.elastic_update(
-        model_id=dummy_id,
-        **new_values.dict(exclude_unset=True)
-    )
-    {% endif %}
-
-
-@router.delete("/{dummy_id}")
-async def delete_dummy_model(dummy_id: uuid.UUID, session: Session = Depends(db_session)) -> None:
-    await session.execute(DummyDBModel.delete(dummy_id))
-    {% if cookiecutter.add_elastic_search == "True" -%}
-    await DummyDBModel.elastic_delete(model_id=dummy_id)
-    {% endif %}
-
-
-@router.get("/", response_model=GetDummyResponse)
-async def filter_dummy_models(
-        dummy_id: Optional[uuid.UUID] = None,
-        name: Optional[str] = None,
-        surname: Optional[str] = None,
-        session: Session = Depends(db_session)
-) -> GetDummyResponse:
-    filter_query = DummyDBModel.filter(
-        dummy_id=dummy_id,
-        name=name,
-        surname=surname
-    )
-    results = await session.fetchall(filter_query)
-    return GetDummyResponse(
-        results=results
-    )
-
-{% if cookiecutter.add_elastic_search == "True" -%}
-@router.get("/elastic", response_model=DummyElasticResponse)
-async def dummy_elastic_filter(query: ElasticFilterModel = Depends(ElasticFilterModel)) -> DummyElasticResponse:
-    results = await DummyDBModel.elastic_filter(**query.dict())
-    return DummyElasticResponse(
-        results=results
-    )
-{% endif %}
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/schema.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/schema.py
deleted file mode 100644
index aec457a..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/dummy_db/schema.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import uuid
-from datetime import datetime
-from typing import Optional, List
-from src.models.dummy_db_model import DummyElasticFilter
-from pydantic import Field, BaseConfig
-from pydantic.main import BaseModel
-
-
-class BaseDummyModel(BaseModel):
-    name: str = Field(example="Dummy name")
-    surname: str = Field(example="Dummy surname")
-
-
-class ReturnDummyModel(BaseDummyModel):
-    id: uuid.UUID
-    created_at: datetime
-    updated_at: datetime
-
-    class Config(BaseConfig):
-        orm_mode = True
-
-
-class GetDummyResponse(BaseModel):
-    results: List[ReturnDummyModel]
-
-{% if cookiecutter.add_elastic_search == "True" -%}
-class DummyElasticResponse(BaseModel):
-    results: List[DummyElasticFilter]
-
-class ElasticAdd(BaseDummyModel):
-    tags: str = Field(default="")
-{% endif %}
-
-class UpdateDummyModel(BaseModel):
-    name: Optional[str] = Field(default=None, example="New name")
-    surname: Optional[str] = Field(default=None, example="New surname")
-    {% if cookiecutter.add_elastic_search == "True" -%}
-    tags: Optional[str] = Field(default=None, example="tag1,tag2")
-    {% endif %}
-
-class DummyFiltersModel(BaseModel):
-    dummy_id: Optional[uuid.UUID] = Field(default=None)
-    name: Optional[str] = Field(default=None)
-    surname: Optional[str] = Field(default=None)
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/httpbin/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/api/httpbin/__init__.py
deleted file mode 100644
index 279f5aa..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/httpbin/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from src.api.httpbin.routes import URL_PREFIX, router
-
-__all__ = ['router', 'URL_PREFIX']
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/httpbin/routes.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/api/httpbin/routes.py
deleted file mode 100644
index f7425c7..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/httpbin/routes.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from fastapi import APIRouter
-
-from src.services.httpbin.client import http_bin_service
-from src.services.httpbin.schema import DummyHttpBinObj, HTTPBinResponse
-
-router = APIRouter()
-URL_PREFIX = "/http_bin"
-
-
-@router.put("/", response_model=HTTPBinResponse)
-async def create_dummy_obj(dummy_obj: DummyHttpBinObj) -> HTTPBinResponse:
-    return await http_bin_service.put_req(dummy_obj)
-
-
-@router.delete("/{obj_id}", response_model=HTTPBinResponse)
-async def delete_dummy_obj(obj_id: str) -> HTTPBinResponse:
-    return await http_bin_service.delete_req(obj_id)
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/httpbin/schema.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/api/httpbin/schema.py
deleted file mode 100644
index e69de29..0000000
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/__init__.py
deleted file mode 100644
index a78ab98..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from src.api.redis_api.routes import router, URL_PREFIX
-
-__all__ = ["router", 'URL_PREFIX']
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/routes.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/routes.py
deleted file mode 100644
index 06a264e..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/routes.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from fastapi import APIRouter, HTTPException
-
-from src.api.redis_api.schema import RedisSetModel, RedisValueModel
-from src.services.redis import redis
-
-router = APIRouter()
-URL_PREFIX = "/redis"
-
-
-@router.put("/set")
-async def set_redis_value(
-        redis_target: RedisSetModel,
-) -> None:
-    set_key = await redis.client.set(redis_target.key, redis_target.value, expire=redis_target.expire)
-    if not set_key:
-        raise HTTPException(
-            status_code=400,
-            detail="No, you can't",
-        )
-
-
-@router.get("/get", response_model=RedisValueModel)
-async def get_redis_value(
-        key: str,
-) -> RedisValueModel:
-    value = await redis.client.get(key, encoding='utf-8')
-    if not value:
-        raise HTTPException(
-            status_code=400,
-            detail="I can't get it",
-        )
-    return RedisValueModel(value=value)
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/schema.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/schema.py
deleted file mode 100644
index 5940df5..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/api/redis_api/schema.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from pydantic import BaseModel, Field
-
-
-class RedisSetModel(BaseModel):
-    key: str
-    value: str
-    expire: int = Field(default=0)
-
-
-class RedisValueModel(BaseModel):
-    value: str
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/exceptions.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/exceptions.py
deleted file mode 100644
index ec7a868..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/exceptions.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from fastapi import HTTPException
-from httpx import Response
-
-
-class ServiceError(HTTPException):
-    """
-    This exception can be returned from handler as is.
-    If this exception is raised you can skip handling.
-
-    Your application will return normal response
-     with status_code=400 and human-readable message.
-    """
-    def __init__(self, action: str, service_response: Response) -> None:
-        try:
-            error_data = service_response.json()
-        except LookupError:
-            error_data = service_response.text
-        self.detail = (
-            f"Can't {action}. Getter respond: {error_data}"
-            f" (status code: {service_response.status_code})"
-        )
-        self.response = service_response
-        self.status_code = 400
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/models/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/models/__init__.py
deleted file mode 100644
index 932064b..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/models/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-{% if cookiecutter.add_dummy_model == "True" -%}
-from src.models.dummy_db_model import DummyDBModel
-
-__all__ = ["DummyDBModel"]
-{% endif %}
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/models/dummy_db_model.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/models/dummy_db_model.py
deleted file mode 100644
index b49086f..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/models/dummy_db_model.py
+++ /dev/null
@@ -1,76 +0,0 @@
-import uuid
-from typing import Optional
-
-from sqlalchemy import Column, String, sql
-{% if cookiecutter.add_elastic_search == "True" -%}
-from src.services.elastic import ElasticModelMixin
-from src.services.elastic.schema import ESReturnModel
-{% endif %}
-from src.services.db import Base
-
-{% if cookiecutter.add_elastic_search == "True" -%}
-class DummyElasticFilter(ESReturnModel):
-    name: str
-    surname: str
-{% endif %}
-
-class DummyDBModel(Base{% if cookiecutter.add_elastic_search == "True" -%}, ElasticModelMixin[DummyElasticFilter]{% endif %}):
-    name = Column(String, nullable=False, index=True)
-    surname = Column(String, nullable=False, index=True)
-    {% if cookiecutter.add_elastic_search == "True" -%}
-    tags = Column(String, nullable=False, default="")
-
-    __es_index_name = "dummy_elastic_index"
-    __es_search_fields = ["name", "surname"]
-    __es_search_type = DummyElasticFilter
-    {% endif %}
-
-    @classmethod
-    def create(
-            cls,
-            *,
-            name: str,
-            surname: str,
-            {% if cookiecutter.add_elastic_search == "True" -%}tags: str = "",{% endif %}
-    ) -> sql.Insert:
-        return cls.insert_query(
-            name=name,
-            surname=surname,
-            {% if cookiecutter.add_elastic_search == "True" -%}tags=tags,{% endif %}
-        )
-
-    @classmethod
-    def delete(cls, dummy_id: uuid.UUID) -> sql.Delete:
-        return cls.delete_query().where(cls.id == dummy_id)
-
-    @classmethod
-    def update(cls,
-               dummy_id: uuid.UUID,
-               *,
-               name: Optional[str] = None,
-               surname: Optional[str] = None,
-               {% if cookiecutter.add_elastic_search == "True" -%}tags: Optional[str] = None,{% endif %}
-               ) -> sql.Update:
-        new_values = {}
-        if name:
-            new_values[cls.name] = name
-        if surname:
-            new_values[cls.surname] = surname
-        if tags is not None:
-            new_values[cls.tags] = tags
-        return cls.update_query().where(cls.id == dummy_id).values(new_values)
-
-    @classmethod
-    def filter(cls, *,
-               dummy_id: Optional[uuid.UUID] = None,
-               name: Optional[str] = None,
-               surname: Optional[str] = None
-               ) -> sql.Select:
-        query = cls.select_query()
-        if dummy_id:
-            query = query.where(cls.id == dummy_id)
-        if name:
-            query = query.where(cls.name == name)
-        if surname:
-            query = query.where(cls.surname == surname)
-        return query
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/server.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/server.py
deleted file mode 100644
index 316edfb..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/server.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import sys
-from typing import Any, Callable
-
-import alembic.config
-from fastapi import FastAPI
-from loguru import logger
-from starlette.requests import Request
-from starlette.responses import UJSONResponse
-
-from src.api import api_router
-from src.services.db import db_engine
-{% if cookiecutter.add_redis == "True" -%}
-from src.services.redis import redis
-{% endif %}
-from src.settings import settings
-
-logger.configure(
-    handlers=[{
-        "sink": sys.stderr,
-        "backtrace": False,
-        "diagnose": settings.is_dev,
-        "catch": False,
-        "colorize": settings.is_dev,
-    }]
-)
-
-app = FastAPI(
-    title="{{cookiecutter.project_name}}",
-    description="{{cookiecutter.project_description}}",
-    default_response_class=UJSONResponse
-)
-
-app.include_router(prefix="", router=api_router)
-
-
-@app.on_event("startup")
-async def startup() -> None:
-    {% if cookiecutter.add_redis == "True" -%}
-    await redis.create_pool()
-    {% endif %}
-    await db_engine.connect()
-
-
-@app.on_event("shutdown")
-async def shutdown() -> None:
-    {% if cookiecutter.add_redis == "True" -%}
-    await redis.shutdown()
-    {% endif %}
-    await db_engine.close()
-
-
-
-@app.middleware("http")
-async def exception_logger(request: Request, call_next: Callable[..., Any]) -> Any:
-    """
-    Just to log all exception with godlike logger from loguru.
-    """
-    target_func = logger.catch()(call_next)
-    response = await target_func(request)
-    return response
-
-
-if settings.is_dev:
-    alembic.config.main(
-        [
-            "--raiseerr",
-            "upgrade",
-            "head",
-        ]
-    )
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/__init__.py
deleted file mode 100644
index 11f3cf1..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from src.services.db.base import Base
-from src.services.db.db_meta import meta
-from src.services.db.engine import db_engine, db_url
-
-__all__ = ["db_engine", "db_url", "meta", "Base"]
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/base.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/base.py
deleted file mode 100644
index 6584a3c..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/base.py
+++ /dev/null
@@ -1,105 +0,0 @@
-import uuid
-from typing import Any, Dict, Optional, Tuple, Type, Union
-
-import sqlalchemy as sa
-from sqlalchemy.dialects.postgresql import UUID
-from sqlalchemy.ext.declarative import as_declarative, declared_attr
-from sqlalchemy.orm.attributes import InstrumentedAttribute
-from src.services.db.db_meta import meta
-
-
-@as_declarative(metadata=meta)
-class Base:
-    """
-    Base class for all models
-
-    It has some very cool methods which allows you
-    to ship autocompletion and type verification to SQLAlchemy models.
-
-    >>> class Model(Base):
-    >>>     name = sa.Column(sa.String())
-    >>>
-    >>>     @classmethod
-    >>>     def get_by_name(cls, name: str) -> sa.sql.Select:
-    >>>         return Model.select_query(cls.id).where(cls.name == name)
-    ...
-    >>> session.fetchall(Model.get_by_name("random_name"))
-
-    `id`, `created_at` and `updated_at` columns are created
-    automatically for all models.
-
-    Basic settings form models such as `__name__`, `__table__` and `__table_args__`
-    defined with types to allow mypy verify this data.
-
-    `__tablename__` generated automatically based on class name.
-    """
-
-    __name__: str
-    __table__: sa.Table
-    __table_args__: Tuple[Any, ...]
-
-    @declared_attr
-    def __tablename__(self) -> str:
-        return self.__name__.lower()
-
-    @declared_attr
-    def id(self) -> Any:
-        return sa.Column(
-            UUID(as_uuid=True),
-            primary_key=True,
-            default=uuid.uuid4
-        )
-
-    @declared_attr
-    def created_at(self) -> Any:
-        return sa.Column(
-            sa.DateTime(timezone=True),
-            server_default=sa.text("clock_timestamp()"),
-            nullable=False,
-        )
-
-    @declared_attr
-    def updated_at(self) -> Any:
-        return sa.Column(
-            sa.DateTime(timezone=True),
-            server_default=sa.text("clock_timestamp()"),
-            onupdate=sa.text("clock_timestamp()"),
-            nullable=False,
-        )
-
-    @classmethod
-    def get(
-        cls, pk: Union[uuid.UUID, str], *fields: InstrumentedAttribute
-    ) -> Optional[Any]:
-        return cls.select_query(*fields).where(cls.id == pk)
-
-    @classmethod
-    def exists(cls, pk: Union[uuid.UUID, str]) -> sa.sql.expression.Exists:
-        return sa.exists().where(cls.id == pk)
-
-    @classmethod
-    def delete(cls, pk: uuid.UUID) -> sa.sql.Delete:
-        return cls.delete_query().where(cls.id == pk)
-
-    @classmethod
-    def select_query(
-        cls,
-        *columns: Union[InstrumentedAttribute, Type["Base"]],
-        use_labels: bool = False,
-    ) -> sa.sql.Select:
-        return sa.select(columns or [cls], use_labels=use_labels)
-
-    @classmethod
-    def insert_query(cls, **values: Any) -> sa.sql.Insert:
-        return cls.__table__.insert().values(**values)
-
-    @classmethod
-    def update_query(cls) -> sa.sql.Update:
-        return cls.__table__.update()
-
-    @classmethod
-    def delete_query(cls) -> sa.sql.Delete:
-        return cls.__table__.delete()
-
-    def as_dict(self) -> Dict[str, Any]:
-        return {c.name: getattr(self, c.key) for c in self.__table__.columns}
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/db_meta.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/db_meta.py
deleted file mode 100644
index 5d24aa1..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/db_meta.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from sqlalchemy import MetaData
-
-meta = MetaData()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/engine.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/engine.py
deleted file mode 100644
index 17c9b02..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/engine.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from typing import Optional
-
-from aiopg.sa import Engine, create_engine
-from sqlalchemy.engine.url import URL, make_url
-
-from src.settings import settings
-
-db_url = make_url(
-    str(URL(
-        drivername=settings.db_driver,
-        username=settings.postgres_user,
-        password=settings.postgres_password,
-        host=settings.postgres_host,
-        port=settings.postgres_port,
-        database=settings.postgres_db,
-    ))
-)
-
-
-class DBEngine:
-    def __init__(self, connection_url: str) -> None:
-        self.dsn = connection_url
-        self.engine: Optional[Engine] = None
-
-    async def connect(self) -> None:
-        self.engine = await create_engine(dsn=self.dsn)
-
-    @property
-    def client(self) -> Engine:
-        if self.engine is None:
-            raise ValueError("Not connected to database")
-        return self.engine
-
-    async def close(self) -> None:
-        if self.engine:
-            self.engine.close()
-            await self.engine.wait_closed()
-        raise Exception("Not connected to database")
-
-
-db_engine = DBEngine(str(db_url))
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/session.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/session.py
deleted file mode 100644
index 45b9fbe..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/db/session.py
+++ /dev/null
@@ -1,61 +0,0 @@
-from typing import Any, List, AsyncGenerator
-
-from aiopg.sa import SAConnection
-
-from src.services.db import db_engine
-
-
-class Session:
-    """
-    Database session object.
-    It used to acquire connection from pool
-    and execute many queries using one connection.
-    """
-    def __init__(self, connection: SAConnection):
-        self.connection = connection
-
-    async def execute(self, query: Any) -> Any:
-        """
-        Simply execute SQLAlchemy generated query
-        or string query and return raw results.
-        """
-        return await self.connection.execute(query)
-
-    async def fetchone(self, query: Any) -> Any:
-        """
-        Get one object from database by query.
-        """
-        cursor = await self.connection.execute(query)
-        return await cursor.fetchone()
-
-    async def scalar(self, query: Any) -> Any:
-        """
-        Scalar returns first column of the first result.
-        It's convenient fot such things as getting function result.
-
-        >>> session = Session(connection)
-        >>> session.scalar(Model.insert_query(**values).returning(Model.id))
-        UUID('7a9ffbfe-f871-42fb-a371-7bd29227a9ff')
-        """
-        result = await self.fetchone(query)
-        return result[0]
-
-    async def fetchall(self, query: Any) -> List[Any]:
-        """
-        Get all matching objects from database by query.
-        """
-        cursor = await self.connection.execute(query)
-        return await cursor.fetchall()
-
-
-async def db_session() -> AsyncGenerator[Session, None]:
-    """
-    Dependency to acquire connection from database pool
-    and close it when handler function is proceeds.
-    """
-    connection = await db_engine.client.acquire()
-    session = Session(connection)
-    try:
-        yield session
-    finally:
-        await connection.close()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/__init__.py
deleted file mode 100644
index 9c2b1be..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from src.services.elastic.mixin import ElasticModelMixin
-
-__all__ = [
-    'ElasticModelMixin',
-    'schema'
-]
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/client.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/client.py
deleted file mode 100644
index 8f89602..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/client.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from elasticsearch import AsyncElasticsearch
-from src.settings import settings
-
-elastic_client = AsyncElasticsearch(hosts=[settings.elastic_host])
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/mixin.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/mixin.py
deleted file mode 100644
index 0b217f9..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/mixin.py
+++ /dev/null
@@ -1,115 +0,0 @@
-import uuid
-from typing import TypeVar, List, Optional, Any, Generic, Union, Dict
-
-from elasticsearch import NotFoundError
-from elasticsearch_dsl import Search
-from elasticsearch_dsl.query import MultiMatch
-from loguru import logger
-
-from src.services.elastic.client import elastic_client
-from src.services.elastic.schema import ESReturnModel
-
-SearchModel = TypeVar("SearchModel", covariant=True, bound=ESReturnModel)
-
-
-class ElasticModelMixin(Generic[SearchModel]):
-    """
-    Mixin to simplify ElasticSearch integration with SQLAlchemy models.
-
-    It has three main variables to override in model.
-
-    1) __es_index_name - it's a name of the index in ElasticSearch where
-        data for model would be stored.
-    2) __es_search_fields - the list of fields use in search query.
-    3) __es_search_type - covariant of `SearchModel` to return from `elastic_filter`.
-        If you won't define it `elastic_filter` will return dict.
-    """
-    __es_index_name: str = "default_index"
-    __es_search_fields: List[str] = []
-    __es_search_type: Optional[SearchModel] = None
-
-    @classmethod
-    async def elastic_add(
-        cls, model_id: uuid.UUID, *, tags: Optional[str] = "", **kwargs: Any
-    ) -> None:
-        """
-        Add object to elastic index.
-
-        >>> await Model.elastic_index(uuid.uuid4(), name="La", surname="Tex")
-        """
-        ret = await elastic_client.index(
-            index=cls.__es_index_name,
-            body={"tags": tags.split(",") if tags else [], **kwargs},
-            id=str(model_id),
-        )
-        result = ret["result"]
-        logger.debug(f"{result} '{cls.__name__}:{model_id}' in elastic")
-
-    @classmethod
-    async def elastic_filter(
-        cls, *, query: str, offset: int, limit: int
-    ) -> List[Union[SearchModel, Dict[str, Any]]]:
-        """
-        Filter existing models in ElasticSearch by string query.
-        This function uses __es_search_fields as fields for phrase_prefix query.
-
-        >>> await Model.elastic_filter(query="La")
-        """
-        elastic_query = Search()
-        if query:
-            elastic_query = elastic_query.query(
-                MultiMatch(
-                    type="phrase_prefix", query=query, fields=cls.__es_search_fields
-                )
-            )
-        elastic_query = elastic_query[offset : offset + limit]
-        search_res = await elastic_client.search(elastic_query.to_dict())
-        hits = search_res.get("hits", {}).get("hits", [])
-        results = []
-        constructor = cls.__es_search_type or dict
-        for hit in hits:
-            logger.debug(hit)
-            results.append(constructor(id=hit.get("_id"), **hit.get("_source", {})))
-        return results
-
-    @classmethod
-    async def elastic_update(
-        cls,
-        model_id: uuid.UUID,
-        **body: Any,
-    ) -> None:
-        """
-        Update model in ElasticSearch by id.
-        """
-        tags = body.get("tags", None)
-        if tags is not None:
-            body["tags"] = tags.split(",") if tags else []
-        logger.debug(f"Updating '{cls.__name__}:{model_id}' in ES")
-        try:
-            await elastic_client.update(
-                index=cls.__es_index_name,
-                id=str(model_id),
-                body={"doc": body},
-                refresh=True,
-            )
-        except NotFoundError:
-            logger.debug(f"Can't update unknown {cls.__name__} in es")
-
-    @classmethod
-    async def elastic_delete(cls, model_id: uuid.UUID) -> None:
-        """
-        Delete object from ElasticSearch index.
-        """
-        try:
-            await elastic_client.delete(index=cls.__es_index_name, id=str(model_id))
-        except NotFoundError:
-            logger.debug(f"'{cls.__name__}:{model_id}' was not found in ES")
-
-    @classmethod
-    async def elastic_create_index(cls) -> None:
-        """
-        Create index if it's not exists.
-        """
-        if not await elastic_client.indices.exists(cls.__es_index_name):
-            logger.debug(f"Creating elastic index {cls.__es_index_name}")
-            await elastic_client.indices.create(cls.__es_index_name)
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/schema.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/schema.py
deleted file mode 100644
index 1b70c8d..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/elastic/schema.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import uuid
-from typing import Optional, List
-
-from pydantic import Field, BaseModel
-
-
-class ESReturnModel(BaseModel):
-    id: uuid.UUID
-    tags: Optional[List[str]]
-
-
-class ElasticFilterModel(BaseModel):
-    query: str = Field(default="")
-    limit: int = Field(default=100, lt=400)
-    offset: int = Field(default=0)
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/httpbin/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/httpbin/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/httpbin/client.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/httpbin/client.py
deleted file mode 100644
index 6676d83..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/httpbin/client.py
+++ /dev/null
@@ -1,60 +0,0 @@
-from httpx import AsyncClient, Response
-from loguru import logger
-
-from src.exceptions import ServiceError
-from src.services.httpbin.schema import DummyHttpBinObj, HTTPBinResponse
-from src.settings import settings
-
-
-class HttpBinService:
-    def __init__(self, http_bin_host: str) -> None:
-        self.host = http_bin_host
-
-    @property
-    def client(self) -> AsyncClient:
-        """
-        Generate a client with base_url
-        :return: async http client
-        """
-        return AsyncClient(
-            base_url=self.host
-        )
-
-    @classmethod
-    def catch_response(cls, response: Response, action: str, *codes: int) -> None:
-        logger.debug("Response got")
-        logger.debug(response.url)
-        logger.debug(response.status_code)
-        logger.debug(response.headers)
-        logger.debug(response.text)
-        if response.status_code not in codes:
-            raise ServiceError(action, response)
-
-    async def put_req(self, dummy_obj: DummyHttpBinObj) -> HTTPBinResponse:
-        """
-        Create dummy object in HTTPBin API.
-        :param dummy_obj: target to create
-        :return: None if success, raise an error if status code is not 201
-        """
-        async with self.client as http:
-            response = await http.put("/put", json=dummy_obj.dict())
-            self.catch_response(response, "Create dummy object", 200)
-        return HTTPBinResponse(**response.json())
-
-    async def delete_req(self, dummy_id: str) -> HTTPBinResponse:
-        """
-        Delete dummy_obj from HTTPBin API.
-        :param dummy_id: target to delete
-        :return: None if success, raise an error if status code is not 200
-        """
-        async with self.client as http:
-            response = await http.delete("/delete", params={
-                "dummy_id": dummy_id
-            })
-            self.catch_response(response, "delete bucket", 200)
-        return HTTPBinResponse(**response.json())
-
-
-http_bin_service = HttpBinService(
-    settings.httpbin_host
-)
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/httpbin/schema.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/httpbin/schema.py
deleted file mode 100644
index 0bbe197..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/httpbin/schema.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from typing import Any, Dict, Optional
-
-from pydantic import BaseModel, Field
-
-
-class DummyHttpBinObj(BaseModel):
-    id: int
-    name: str
-    surname: str
-
-
-class HTTPBinResponse(BaseModel):
-    args: Dict[str, Any]
-    data: str
-    files: Dict[str, Any]
-    form: Dict[str, str]
-    headers: Dict[str, str]
-    json_data: Optional[Dict[str, Any]] = Field(alias="json")
-    origin: str
-    url: str
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/redis.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/services/redis.py
deleted file mode 100644
index 33dfd60..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/services/redis.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from typing import Optional, Tuple, Union
-
-import aioredis
-from aioredis import Redis
-from loguru import logger
-from src.settings import settings
-
-
-class RedisClient:
-    """
-    Super simple redis client.
-    """
-    def __init__(self, address: Union[str, Tuple[str, int]], password: str):
-        self.address = address
-        self.password = password
-        self._client: Optional[Redis] = None
-
-    async def create_pool(self) -> None:
-        logger.debug("Creating redis pool")
-        self._client = await aioredis.create_redis_pool(
-            self.address, password=self.password
-        )
-        logger.debug("Redis pool created")
-
-    @property
-    def client(self) -> Redis:
-        """
-        Get actual Redis client.
-        :return:
-        """
-        if self._client is None:
-            raise Exception("Not connected to redis")
-        return self._client
-
-    async def shutdown(self) -> None:
-        """
-        Shutdown session for redis.
-        Close redis pool.
-        """
-        logger.debug("Shutting down redis")
-        if self._client is None:
-            return
-        self._client.close()
-        await self._client.wait_closed()
-        logger.debug("Redis connection pool closed")
-
-
-redis = RedisClient(
-    address=(settings.redis_host, settings.redis_port), password=settings.redis_password
-)
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/settings.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/settings.py
deleted file mode 100644
index 73121a3..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/src/settings.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from enum import Enum
-
-from pydantic import BaseSettings, Field
-
-
-class AppMode(Enum):
-    DEV = "development"
-    PROD = "production"
-
-
-class Settings(BaseSettings):
-    # Application settings
-    fastapi_env: AppMode = Field(default=AppMode.PROD)
-    log_level: str = Field(default="INFO")
-    # Auxiliary database settings
-    db_echo: bool = Field(default=False)
-    db_driver: str = Field(default="postgresql")
-    # Postgres connection settings
-    postgres_db: str = Field(...)
-    postgres_host: str = Field(...)
-    postgres_port: str = Field(...)
-    postgres_user: str = Field(...)
-    postgres_password: str = Field(...)
-    {% if cookiecutter.add_redis == "True" -%}
-    # Redis connection settings
-    redis_host: str = Field(...)
-    redis_port: int = Field(default=6379)
-    redis_password: str = Field(...)
-    {% endif %}
-    {% if cookiecutter.add_scheduler == "True" -%}
-    schedule_timer: int = Field(default=20)
-    {% endif %}
-    # httpbin client settings
-    httpbin_host: str = Field(default="https://httpbin.org/")
-    {% if cookiecutter.add_elastic_search == "True" -%}
-    elastic_host: str = Field(...)
-    {% endif %}
-
-    @property
-    def is_dev(self) -> bool:
-        return self.fastapi_env == AppMode.DEV
-
-
-settings = Settings()
\ No newline at end of file
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/utils.py b/fastapi_template/template/{{cookiecutter.project_name}}/src/utils.py
deleted file mode 100644
index e69de29..0000000
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/systemd/{{ cookiecutter.project_name }}_scheduler.service b/fastapi_template/template/{{cookiecutter.project_name}}/systemd/{{ cookiecutter.project_name }}_scheduler.service
deleted file mode 100644
index 5da9aa8..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/systemd/{{ cookiecutter.project_name }}_scheduler.service	
+++ /dev/null
@@ -1,15 +0,0 @@
-[Unit]
-Description={{cookiecutter.project_description}}
-After=network.target
-
-[Service]
-Type=simple
-User=gitlab-runner
-EnvironmentFile=ENV_FILE
-WorkingDirectory=DIR
-ExecStart=/bin/bash -c 'source /opt/rh/rh-python38/enable && poetry install --no-dev && poetry run python scheduler.py'
-ExecStop=/usr/bin/kill -15 $MAINPID
-Restart=on-abort
-
-[Install]
-WantedBy=multi-user.target
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/systemd/{{ cookiecutter.project_name }}_service.service b/fastapi_template/template/{{cookiecutter.project_name}}/systemd/{{ cookiecutter.project_name }}_service.service
deleted file mode 100644
index 9b624ec..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/systemd/{{ cookiecutter.project_name }}_service.service	
+++ /dev/null
@@ -1,15 +0,0 @@
-[Unit]
-Description={{cookiecutter.project_description}}
-After=network.target
-
-[Service]
-Type=simple
-User=gitlab-runner
-EnvironmentFile=ENV_FILE
-WorkingDirectory=DIR
-ExecStart=/bin/bash -c 'source /opt/rh/rh-python38/enable && poetry install --no-dev && poetry run uvicorn --workers=4 --access-log --log-level debug --host 0.0.0.0 --port 8100 src.server:app'
-ExecStop=/usr/bin/kill -15 $MAINPID
-Restart=on-abort
-
-[Install]
-WantedBy=multi-user.target
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/tests/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/tests/__init__.py
deleted file mode 100644
index 9982356..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/tests/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from typing import Any, Dict, Optional
-
-from pydantic import Field
-from pydantic.main import BaseModel
-
-
-class TestSettings(BaseModel):
-    __test__ = False
-
-    request_data: Dict[str, Any]
-    response_code: Optional[int]
-    response_json: Optional[Dict[Any, Any]] = Field(None)
-    validation_data: Optional[Dict[Any, Any]] = Field(None)
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/tests/conftest.py b/fastapi_template/template/{{cookiecutter.project_name}}/tests/conftest.py
deleted file mode 100644
index 7c2596e..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/tests/conftest.py
+++ /dev/null
@@ -1,93 +0,0 @@
-import warnings
-from typing import Generator
-
-import alembic.config
-import pytest
-from fastapi.testclient import TestClient
-from sqlalchemy import create_engine
-from sqlalchemy.engine import Connection, Engine
-from sqlalchemy.engine.url import URL, make_url
-from sqlalchemy.exc import ProgrammingError
-
-from src.services.db import db_url
-from src.settings import settings
-
-warnings.filterwarnings("ignore", category=DeprecationWarning)
-
-
-def get_engine() -> Engine:
-    pg_db_url = make_url(
-        str(URL(
-            drivername=settings.db_driver,
-            username=settings.postgres_user,
-            password=settings.postgres_password,
-            host=settings.postgres_host,
-            port=settings.postgres_port,
-            database="postgres",
-        ))
-    )
-    engine = create_engine(str(pg_db_url))
-    return engine
-
-
-def run_psql_without_transaction(command: str) -> None:
-    engine = get_engine()
-    connection = engine.connect()
-    connection.connection.set_isolation_level(0)
-    connection.execute(command)
-    connection.connection.set_isolation_level(1)
-    connection.close()
-
-
-@pytest.fixture(scope="session")
-def create_database() -> None:
-    try:
-        run_psql_without_transaction(f"CREATE DATABASE {settings.postgres_db}")
-    except ProgrammingError:
-        pass
-
-
-def prepare_db() -> None:
-    alembic.config.main(
-        [
-            "--raiseerr",
-            "upgrade",
-            "head",
-        ]
-    )
-
-
-def drop_db() -> None:
-    alembic.config.main(
-        [
-            "--raiseerr",
-            "downgrade",
-            "base",
-        ]
-    )
-
-
-@pytest.fixture(scope="function")
-def create_db(create_database: None) -> Generator[None, None, None]:
-    prepare_db()
-    yield
-    drop_db()
-
-
-@pytest.fixture(scope="function")
-def app_fixture(create_db: None) -> TestClient:
-    from src.server import app
-
-    api_client = TestClient(app)
-
-    return api_client
-
-
-@pytest.fixture(scope="function")
-def pg_conn() -> Generator[Connection, None, None]:
-    engine = create_engine(str(db_url), pool_size=0, echo=True)
-    conn = engine.connect()
-
-    yield conn
-
-    conn.close()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/tests/dummy_db_test.py b/fastapi_template/template/{{cookiecutter.project_name}}/tests/dummy_db_test.py
deleted file mode 100644
index 89bb371..0000000
--- a/fastapi_template/template/{{cookiecutter.project_name}}/tests/dummy_db_test.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import pytest
-import sqlalchemy as sa
-from sqlalchemy.engine import Connection
-from starlette.testclient import TestClient
-
-from src.models import DummyDBModel
-from tests import TestSettings
-
-test_data = [
-    TestSettings(
-        request_data=dict(
-            dummy_id="b4439fc8-8fde-4079-ab2c-d61e46e22f25",
-            json=dict(
-                name="test dummy object",
-                surname="Cesar"
-            ),
-        ),
-    ),
-]
-
-
-@pytest.mark.parametrize("test_conf", test_data)
-def test_delete_dummy_obj(
-        pg_conn: Connection, test_conf: TestSettings, app_fixture: TestClient
-) -> None:
-    with app_fixture as client:
-        put_result = client.put(
-            "/dummy_db_obj/",
-            json=test_conf.request_data["json"],
-        )
-        assert put_result.status_code == 200
-        with pg_conn.begin():
-            result = pg_conn.execute(DummyDBModel.select_query(DummyDBModel.id)).first()
-            assert len(result) == 1
-        result = client.delete(f"/dummy_db_obj/{result[0]}")
-        assert result.status_code == 200
-        with pg_conn.begin():
-            result = pg_conn.execute(sa.func.count(DummyDBModel.id)).first()
-            assert result[0] == 0
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/__init__.py
new file mode 100644
index 0000000..8b08592
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/__init__.py
@@ -0,0 +1 @@
+"""{{cookiecutter.project_name}} package."""
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/__main__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/__main__.py
new file mode 100644
index 0000000..cdb27c1
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/__main__.py
@@ -0,0 +1,19 @@
+import uvicorn
+
+from {{cookiecutter.project_name}}.settings import settings
+
+
+def main() -> None:
+    """Entrypoint of the application."""
+    uvicorn.run(
+        "{{cookiecutter.project_name}}.web.application:get_app",
+        workers=settings.workers_count,
+        host=settings.host,
+        port=settings.port,
+        reload=settings.reload,
+        factory=True,
+    )
+
+
+if __name__ == "__main__":
+    main()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/conftest.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/conftest.py
new file mode 100644
index 0000000..86b6683
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/conftest.py
@@ -0,0 +1,54 @@
+import asyncio
+import sys
+from typing import Any, Generator
+
+import pytest
+
+from {{cookiecutter.project_name}}.settings import settings
+
+
+@pytest.fixture(scope="session")
+def event_loop() -> Generator[asyncio.AbstractEventLoop, None, None]:
+    """
+    Create an instance of event loop for tests.
+
+    This hack is required in order to get `dbsession` fixture to work.
+    Because default fixture `event_loop` is function scoped,
+    but dbsession requires session scoped `event_loop` fixture.
+
+    :yields: event loop.
+    """
+    python_version = sys.version_info[:2]
+    if sys.platform.startswith("win") and python_version >= (3, 8):
+        # Avoid "RuntimeError: Event loop is closed" on Windows when tearing down tests
+        # https://github.com/encode/httpx/issues/914
+        asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
+
+    loop = asyncio.new_event_loop()
+    yield loop
+    loop.close()
+
+
+@pytest.fixture(scope="session")
+def database_url() -> str:
+    """
+    Возвращает ссылку на тестовю базу данных.
+
+    :return: URL.
+    """
+    return str(settings.db_url)
+
+
+@pytest.fixture(scope="session")
+def init_database() -> Any:
+    """
+    Create a database for tests.
+
+    :return: ничего
+    """
+    from {{cookiecutter.project_name}}.db.meta import meta  # noqa: WPS433
+    from {{cookiecutter.project_name}}.db.models import load_all_models  # noqa: WPS433
+
+    load_all_models()
+
+    return meta.create_all
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/base.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/base.py
new file mode 100644
index 0000000..3689d8e
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/base.py
@@ -0,0 +1,20 @@
+from typing import Any, Tuple
+
+from sqlalchemy import Table
+from sqlalchemy.orm import as_declarative
+
+from {{cookiecutter.project_name}}.db.meta import meta
+
+
+@as_declarative(metadata=meta)
+class Base:
+    """
+    Base for all models.
+
+    It has some type definitions to
+    enhance autocompletion.
+    """
+
+    __tablename__: str
+    __table__: Table
+    __table_args__: Tuple[Any, ...]
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dao/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dao/__init__.py
new file mode 100644
index 0000000..db62a0a
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dao/__init__.py
@@ -0,0 +1 @@
+"""DAO classes."""
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dao/dummy_dao.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dao/dummy_dao.py
new file mode 100644
index 0000000..2180ffd
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dao/dummy_dao.py
@@ -0,0 +1,35 @@
+from fastapi import Depends
+from sqlalchemy import select
+from sqlalchemy.ext.asyncio import AsyncScalarResult, AsyncSession
+
+from {{cookiecutter.project_name}}.db.dependencies import get_db_session
+from {{cookiecutter.project_name}}.db.models.dummy_model import DummyModel
+
+
+class DummyDAO:
+    """Class for accessing dummy table."""
+
+    def __init__(self, session: AsyncSession = Depends(get_db_session)):
+        self.session = session
+
+    async def create_dummy_model(self, name: str) -> None:
+        """
+        Add single dummy to session.
+
+        :param name: name of a dummy.
+        """
+        self.session.add(DummyModel(name=name))
+
+    async def get_all_dummies(self, limit: int, offset: int) -> AsyncScalarResult:
+        """
+        Get all dummy models with limit/offset pagination.
+
+        :param limit: limit of dummies.
+        :param offset: offset of dummies.
+        :return: stream of dummies.
+        """
+        raw_stream = await self.session.stream(
+            select(DummyModel).limit(limit).offset(offset),
+        )
+
+        return raw_stream.scalars()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dependencies.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dependencies.py
new file mode 100644
index 0000000..9e511d7
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/dependencies.py
@@ -0,0 +1,19 @@
+from typing import AsyncGenerator
+
+from sqlalchemy.ext.asyncio import AsyncSession
+from starlette.requests import Request
+
+
+async def get_db_session(request: Request) -> AsyncGenerator[AsyncSession, None]:
+    """
+    Create and get database session.
+
+    :param request: current request.
+    :yield: database session.
+    """
+    session: AsyncSession = request.app.state.db_session_factory()
+
+    yield session
+
+    await session.commit()
+    await session.close()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/meta.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/meta.py
new file mode 100644
index 0000000..4c95254
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/meta.py
@@ -0,0 +1,3 @@
+import sqlalchemy as sa
+
+meta = sa.MetaData()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/migrations/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/README.md
similarity index 100%
rename from fastapi_template/template/{{cookiecutter.project_name}}/migrations/__init__.py
rename to fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/README.md
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/__init__.py
new file mode 100644
index 0000000..6dccb95
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/__init__.py
@@ -0,0 +1 @@
+"""Alembic migraions."""
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/env.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/env.py
new file mode 100644
index 0000000..6507aa0
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/env.py
@@ -0,0 +1,85 @@
+import asyncio
+from logging.config import fileConfig
+
+from alembic import context
+from sqlalchemy.ext.asyncio.engine import create_async_engine
+from sqlalchemy.future import Connection
+
+from {{cookiecutter.project_name}}.db.meta import meta
+from {{cookiecutter.project_name}}.db.models import load_all_models
+from {{cookiecutter.project_name}}.settings import settings
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+
+load_all_models()
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+fileConfig(config.config_file_name)
+
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+target_metadata = meta
+
+# other values from the config, defined by the needs of env.py,
+# can be acquired:
+# my_important_option = config.get_main_option("my_important_option")
+# ... etc.
+
+
+def run_migrations_offline() -> None:
+    """Run migrations in 'offline' mode.
+
+    This configures the context with just a URL
+    and not an Engine, though an Engine is acceptable
+    here as well.  By skipping the Engine creation
+    we don't even need a DBAPI to be available.
+
+    Calls to context.execute() here emit the given string to the
+    script output.
+
+    """
+    context.configure(
+        url=str(settings.db_url),
+        target_metadata=target_metadata,
+        literal_binds=True,
+        dialect_opts={"paramstyle": "named"},
+    )
+
+    with context.begin_transaction():
+        context.run_migrations()
+
+
+def do_run_migrations(connection: Connection) -> None:
+    """
+    Run actual sync migrations.
+
+    :param connection: connection to the database.
+    """
+    context.configure(connection=connection, target_metadata=target_metadata)
+
+    with context.begin_transaction():
+        context.run_migrations()
+
+
+async def run_migrations_online() -> None:
+    """
+    Run migrations in 'online' mode.
+
+    In this scenario we need to create an Engine
+    and associate a connection with the context.
+    """
+    connectable = create_async_engine(str(settings.db_url))
+
+    async with connectable.connect() as connection:
+        await connection.run_sync(do_run_migrations)
+
+
+if context.is_offline_mode():
+    run_migrations_offline()
+else:
+    asyncio.run(run_migrations_online())
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/migrations/script.py.mako b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/script.py.mako
similarity index 90%
rename from fastapi_template/template/{{cookiecutter.project_name}}/migrations/script.py.mako
rename to fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/script.py.mako
index 2c01563..55df286 100644
--- a/fastapi_template/template/{{cookiecutter.project_name}}/migrations/script.py.mako
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/script.py.mako
@@ -16,9 +16,9 @@ branch_labels = ${repr(branch_labels)}
 depends_on = ${repr(depends_on)}
 
 
-def upgrade():
+def upgrade() -> None:
     ${upgrades if upgrades else "pass"}
 
 
-def downgrade():
+def downgrade() -> None:
     ${downgrades if downgrades else "pass"}
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/versions/2021-08-16-16-53_819cbf6e030b.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/versions/2021-08-16-16-53_819cbf6e030b.py
new file mode 100644
index 0000000..3b22742
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/versions/2021-08-16-16-53_819cbf6e030b.py
@@ -0,0 +1,22 @@
+"""Initial migration.
+
+Revision ID: 819cbf6e030b
+Revises:
+Create Date: 2021-08-16 16:53:05.484024
+
+"""
+
+
+# revision identifiers, used by Alembic.
+revision = "819cbf6e030b"
+down_revision = None
+branch_labels = None
+depends_on = None
+
+
+def upgrade() -> None:
+    pass
+
+
+def downgrade() -> None:
+    pass
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/versions/2021-08-16-16-55_2b7380507a71.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/versions/2021-08-16-16-55_2b7380507a71.py
new file mode 100644
index 0000000..53cfe12
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/versions/2021-08-16-16-55_2b7380507a71.py
@@ -0,0 +1,32 @@
+"""Created Dummy Model.
+
+Revision ID: 2b7380507a71
+Revises: 819cbf6e030b
+Create Date: 2021-08-16 16:55:25.157309
+
+"""
+import sqlalchemy as sa
+from alembic import op
+
+# revision identifiers, used by Alembic.
+revision = "2b7380507a71"
+down_revision = "819cbf6e030b"
+branch_labels = None
+depends_on = None
+
+
+def upgrade() -> None:
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.create_table(
+        "dummy_model",
+        sa.Column("id", sa.Integer(), autoincrement=True, nullable=False),
+        sa.Column("name", sa.String(length=200), nullable=True),
+        sa.PrimaryKeyConstraint("id"),
+    )
+    # ### end Alembic commands ###
+
+
+def downgrade() -> None:
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_table("dummy_model")
+    # ### end Alembic commands ###
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/src/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/versions/__init__.py
similarity index 100%
rename from fastapi_template/template/{{cookiecutter.project_name}}/src/__init__.py
rename to fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/migrations/versions/__init__.py
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/models/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/models/__init__.py
new file mode 100644
index 0000000..490649f
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/models/__init__.py
@@ -0,0 +1,14 @@
+"""{{cookiecutter.project_name}} models."""
+import pkgutil
+from pathlib import Path
+
+
+def load_all_models() -> None:
+    """Load all models from this folder."""
+    package_dir = Path(__file__).resolve().parent
+    modules = pkgutil.walk_packages(
+        path=[str(package_dir)],
+        prefix="{{cookiecutter.project_name}}.db.models.",
+    )
+    for module in modules:
+        __import__(module.name)  # noqa: WPS421
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/models/dummy_model.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/models/dummy_model.py
new file mode 100644
index 0000000..15d56f5
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/db/models/dummy_model.py
@@ -0,0 +1,13 @@
+from sqlalchemy.sql.schema import Column
+from sqlalchemy.sql.sqltypes import Integer, String
+
+from {{cookiecutter.project_name}}.db.base import Base
+
+
+class DummyModel(Base):
+    """Model for demo purpose."""
+
+    __tablename__ = "dummy_model"
+
+    id = Column(Integer(), primary_key=True, autoincrement=True)
+    name = Column(String(length=200))  # noqa: WPS432
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/services/redis/dependency.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/services/redis/dependency.py
new file mode 100644
index 0000000..986d336
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/services/redis/dependency.py
@@ -0,0 +1,20 @@
+from typing import AsyncGenerator
+
+from aioredis import Redis
+from starlette.requests import Request
+
+
+async def get_redis_connection(request: Request) -> AsyncGenerator[Redis, None]:
+    """
+    Get redis client.
+
+    This dependency aquires connection from pool.
+
+    :param request: current request.
+    :yield:  redis client.
+    """
+    redis_client = Redis(connection_pool=request.app.state.redis_pool)
+
+    yield redis_client
+
+    await redis_client.close()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/settings.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/settings.py
new file mode 100644
index 0000000..03e2368
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/settings.py
@@ -0,0 +1,94 @@
+from pathlib import Path
+from tempfile import gettempdir
+from typing import Optional
+
+from pydantic import BaseSettings
+from yarl import URL
+
+TEMP_DIR = Path(gettempdir())
+
+
+class Settings(BaseSettings):
+    """Application settings."""
+
+    host: str = "127.0.0.1"
+    port: int = 8000
+    # quantity of workers for uvicorn
+    workers_count: int = 1
+    # Enable uvicorn reloading
+    reload: bool = False
+
+    {%- if cookiecutter.db_info.name != "none" %}
+    {%- if cookiecutter.db_info.name == "sqlite" %}
+    db_file: Path = TEMP_DIR / "db.sqlite3"
+    {% else %}
+    db_host: str = "{{cookiecutter.project_name}}-db"
+    db_port: int = {{cookiecutter.db_info.port}}
+    db_user: str = "{{cookiecutter.project_name}}"
+    db_pass: str = "{{cookiecutter.project_name}}"
+    db_base: str = "{{cookiecutter.project_name}}"
+    db_echo: bool = False
+    {%- endif %}
+    {%- endif %}
+
+    {%- if cookiecutter.enable_redis == "True" %}
+    redis_host: str = "{{cookiecutter.project_name}}-redis"
+    redis_port: int = 6379
+    redis_user: Optional[str] = None
+    redis_pass: Optional[str] = None
+    redis_base: Optional[int] = None
+    {% endif %}
+
+    {%- if cookiecutter.db_info.name != "none" %}
+    @property
+    def db_url(self) -> URL:
+        """
+        Assemble database URL from settings.
+
+        :return: database URL.
+        """
+        {%- if cookiecutter.db_info.name == "sqlite" %}
+        return URL.build(
+            scheme="{{cookiecutter.db_info.driver}}",
+            path=f"///{self.db_file}"
+        )
+        {% else %}
+        return URL.build(
+            scheme="{{cookiecutter.db_info.driver}}",
+            host=self.db_host,
+            port=self.db_port,
+            user=self.db_user,
+            password=self.db_pass,
+            path=f"/{self.db_base}",
+        )
+        {%- endif %}
+    {%- endif %}
+
+    {%- if cookiecutter.enable_redis == "True" %}
+    @property
+    def redis_url(self) -> URL:
+        """
+        Assemble REDIS URL from settings.
+
+        :return: redis URL.
+        """
+        path = ""
+        if self.redis_base is not None:
+            path = f"/{self.redis_base}"
+        return URL.build(
+            scheme="redis",
+            host=self.redis_host,
+            port=self.redis_port,
+            user=self.redis_user,
+            password=self.redis_pass,
+            path=path,
+        )
+    {%- endif %}
+
+    class Config:
+        env_file = ".env"
+        env_prefix = "{{cookiecutter.project_name | upper }}_"
+        env_file_encoding = "utf-8"
+
+
+settings = Settings()
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/__init__.py
new file mode 100644
index 0000000..36120ba
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/__init__.py
@@ -0,0 +1 @@
+"""Tests for {{cookiecutter.project_name}}."""
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/test_fastapitestproject.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/test_fastapitestproject.py
new file mode 100644
index 0000000..23a09e9
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/tests/test_fastapitestproject.py
@@ -0,0 +1,8 @@
+def test_stub() -> None:
+    """
+    Test stub.
+
+    It must be removed in real applications.
+    """
+    test_val = 3
+    assert test_val == 3
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/__init__.py
new file mode 100644
index 0000000..35232ba
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/__init__.py
@@ -0,0 +1 @@
+"""{{cookiecutter.project_name}} API package."""
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/__init__.py
new file mode 100644
index 0000000..5b22f56
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/__init__.py
@@ -0,0 +1,4 @@
+"""Dummy model API."""
+from {{cookiecutter.project_name}}.web.api.dummy.views import router
+
+__all__ = ['router']
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/schema.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/schema.py
new file mode 100644
index 0000000..129518d
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/schema.py
@@ -0,0 +1,21 @@
+from pydantic import BaseModel
+
+
+class DummyModelDTO(BaseModel):
+    """
+    DTO for dummy models.
+
+    It returned when accessing dummy models from the API.
+    """
+
+    id: int
+    name: str
+
+    class Config:
+        orm_mode = True
+
+
+class DummyModelInputDTO(BaseModel):
+    """DTO for creating new dummy model."""
+
+    name: str
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/views.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/views.py
new file mode 100644
index 0000000..955c9c6
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/dummy/views.py
@@ -0,0 +1,41 @@
+from typing import Any, List
+
+from fastapi import APIRouter
+from fastapi.param_functions import Depends
+
+from {{cookiecutter.project_name}}.db.dao.dummy_dao import DummyDAO
+from {{cookiecutter.project_name}}.web.api.dummy.schema import DummyModelDTO, DummyModelInputDTO
+
+router = APIRouter()
+
+
+@router.get("/", response_model=List[DummyModelDTO])
+async def get_dummy_models(
+    limit: int = 10,
+    offset: int = 0,
+    dummy_dao: DummyDAO = Depends(),
+) -> Any:
+    """
+    Retrieve all dummy objects from database.
+
+    :param limit: limit of dummy objects, defaults to 10.
+    :param offset: offset of dummy objects, defaults to 0.
+    :param dummy_dao: DAO for dummy models.
+    :return: list of dummy obbjects from database.
+    """
+    dummies = await dummy_dao.get_all_dummies(limit=limit, offset=offset)
+    return await dummies.fetchall()
+
+
+@router.put("/")
+async def create_dummy_model(
+    new_dummy_object: DummyModelInputDTO,
+    dummy_dao: DummyDAO = Depends(),
+) -> None:
+    """
+    Create dummy model in database.
+
+    :param new_dummy_object: new dummy model item.
+    :param dummy_dao: DAO for dummy models.
+    """
+    await dummy_dao.create_dummy_model(**new_dummy_object.dict())
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/__init__.py
new file mode 100644
index 0000000..06767af
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/__init__.py
@@ -0,0 +1,4 @@
+"""Echo API."""
+from {{cookiecutter.project_name}}.web.api.echo.views import router
+
+__all__ = ['router']
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/schema.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/schema.py
new file mode 100644
index 0000000..f408bc2
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/schema.py
@@ -0,0 +1,7 @@
+from pydantic import BaseModel
+
+
+class Message(BaseModel):
+    """Simple message model."""
+
+    message: str
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/views.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/views.py
new file mode 100644
index 0000000..52d90c6
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/echo/views.py
@@ -0,0 +1,18 @@
+from fastapi import APIRouter
+
+from {{cookiecutter.project_name}}.web.api.echo.schema import Message
+
+router = APIRouter()
+
+
+@router.post("/", response_model=Message)
+async def send_echo_message(
+    incoming_message: Message,
+) -> Message:
+    """
+    Send echo back to user.
+
+    :param incoming_message: incoming message.
+    :returns: message same as the incoming.
+    """
+    return incoming_message
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/__init__.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/__init__.py
new file mode 100644
index 0000000..6c1178b
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/__init__.py
@@ -0,0 +1,4 @@
+"""Redis API."""
+from {{cookiecutter.project_name}}.web.api.redis.views import router
+
+__all__ = ['router']
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/schema.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/schema.py
new file mode 100644
index 0000000..e7e025d
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/schema.py
@@ -0,0 +1,10 @@
+from typing import Optional
+
+from pydantic import BaseModel
+
+
+class RedisValueDTO(BaseModel):
+    """DTO for redis values."""
+
+    key: str
+    value: Optional[str]  # noqa: WPS110
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/views.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/views.py
new file mode 100644
index 0000000..aeb4d0d
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/redis/views.py
@@ -0,0 +1,41 @@
+from aioredis import Redis
+from fastapi import APIRouter
+from fastapi.param_functions import Depends
+
+from {{cookiecutter.project_name}}.services.redis.dependency import get_redis_connection
+from {{cookiecutter.project_name}}.web.api.redis.schema import RedisValueDTO
+
+router = APIRouter()
+
+
+@router.get("/", response_model=RedisValueDTO)
+async def get_value(
+    key: str,
+    redis: Redis = Depends(get_redis_connection),
+) -> RedisValueDTO:
+    """
+    Get value from redis.
+
+    :param key: redis key, to get data from.
+    :param redis: redis connection.
+    :returns: information from redis.
+    """
+    redis_value = await redis.get(key)
+    return RedisValueDTO(
+        key=key,
+        value=redis_value,
+    )
+
+
+@router.put("/")
+async def set_value(
+    redis_value: RedisValueDTO,
+    redis: Redis = Depends(get_redis_connection),
+) -> None:
+    """
+    Set value in redis.
+
+    :param redis_value: new value data.
+    :param redis: redis connection.
+    """
+    await redis.set(name=redis_value.key, value=redis_value.value)
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/router.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/router.py
new file mode 100644
index 0000000..58cb9c2
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/api/router.py
@@ -0,0 +1,19 @@
+from fastapi.routing import APIRouter
+
+from {{cookiecutter.project_name}}.web.api import echo
+
+{%- if cookiecutter.db_info.name != "none" %}
+from {{cookiecutter.project_name}}.web.api import dummy
+{%- endif %}
+{%- if cookiecutter.enable_redis == "True" %}
+from {{cookiecutter.project_name}}.web.api import redis
+{%- endif %}
+
+api_router = APIRouter()
+api_router.include_router(echo.router, prefix="/echo", tags=["echo"])
+{%- if cookiecutter.db_info.name != "none" %}
+api_router.include_router(dummy.router, prefix="/dummy", tags=["dummy"])
+{%- endif %}
+{%- if cookiecutter.enable_redis == "True" %}
+api_router.include_router(redis.router, prefix="/redis", tags=["redis"])
+{%- endif %}
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/application.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/application.py
new file mode 100644
index 0000000..8de1571
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/application.py
@@ -0,0 +1,24 @@
+from fastapi import FastAPI
+
+from {{cookiecutter.project_name}}.web.api.router import api_router
+from {{cookiecutter.project_name}}.web.lifetime import shutdown, startup
+
+
+def get_app() -> FastAPI:
+    """
+    Get FastAPI application.
+
+    This is the main constructor of an application.
+
+    :return: application.
+    """
+    app = FastAPI(
+        title="{{cookiecutter.project_name}}",
+    )
+
+    app.on_event("startup")(startup(app))
+    app.on_event("shutdown")(shutdown(app))
+
+    app.include_router(router=api_router, prefix="/api")
+
+    return app
diff --git a/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py
new file mode 100644
index 0000000..15a4ea8
--- /dev/null
+++ b/fastapi_template/template/{{cookiecutter.project_name}}/{{cookiecutter.project_name}}/web/lifetime.py
@@ -0,0 +1,94 @@
+from typing import Awaitable, Callable
+
+from fastapi import FastAPI
+
+from {{cookiecutter.project_name}}.settings import settings
+
+{%- if cookiecutter.enable_redis == "True" %}
+import aioredis
+{%- endif %}
+
+{%- if cookiecutter.db_info.name != "none" %}
+from asyncio import current_task
+from sqlalchemy.ext.asyncio import (  # type: ignore
+    AsyncSession,
+    async_scoped_session,
+    create_async_engine,
+)
+from sqlalchemy.orm import sessionmaker
+{%- endif %}
+
+
+{% if cookiecutter.db_info.name != "none" %}
+def _setup_db(app: FastAPI) -> None:
+    """
+    Create connection to the database.
+
+    This function creates SQLAlchemy engine instance,
+    session_factory for creating sessions
+    and stores them in the application's state property.
+
+    :param app: fastAPI application.
+    """
+    engine = create_async_engine(str(settings.db_url), echo=settings.db_echo)
+    session_factory = async_scoped_session(
+        sessionmaker(
+            engine,
+            expire_on_commit=False,
+            class_=AsyncSession,
+        ),
+        scopefunc=current_task,
+    )
+    app.state.db_engine = engine
+    app.state.db_session_factory = session_factory
+{%- endif %}
+
+{% if cookiecutter.enable_redis == "True" %}
+def _setup_redis(app: FastAPI) -> None:
+    app.state.redis_pool = aioredis.ConnectionPool.from_url(
+        str(settings.redis_url),
+    )
+{%- endif %}
+
+
+def startup(app: FastAPI) -> Callable[[], Awaitable[None]]:
+    """
+    Actions to run on application startup.
+
+    This function use fastAPI app to store data,
+    such as db_engine.
+
+    :param app: the fastAPI application.
+    :return: function that actually performs actions.
+    """
+
+    async def _startup() -> None:
+        {%- if cookiecutter.db_info.name != "none" %}
+        _setup_db(app)
+        {%- endif %}
+        {%- if cookiecutter.enable_redis == "True" %}
+        _setup_redis(app)
+        {%- endif %}
+        pass  # noqa: WPS420
+
+    return _startup
+
+
+def shutdown(app: FastAPI) -> Callable[[], Awaitable[None]]:
+    """
+    Actions to run on application's shutdown.
+
+    :param app: fastAPI application.
+    :return: function that actually performs actions.
+    """
+
+    async def _shutdown() -> None:
+        {%- if cookiecutter.db_info.name != "none" %}
+        await app.state.db_engine.dispose()
+        {%- endif %}
+        {%- if cookiecutter.enable_redis == "True" %}
+        await app.state.redis_pool.disconnect()
+        {%- endif %}
+        pass  # noqa: WPS420
+
+    return _shutdown
diff --git a/images/ui-example.png b/images/ui-example.png
new file mode 100644
index 0000000000000000000000000000000000000000..7d2a6c08766a6f418073b6d0a5a2eb8d38d264e0
GIT binary patch
literal 23550
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

literal 0
HcmV?d00001

diff --git a/poetry.lock b/poetry.lock
index 9f69da8..17bcbf8 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,22 +1,26 @@
-[[package]]
-name = "appdirs"
-version = "1.4.4"
-description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
-category = "main"
-optional = false
-python-versions = "*"
-
 [[package]]
 name = "arrow"
-version = "0.17.0"
+version = "1.1.1"
 description = "Better dates & times for Python"
 category = "main"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.6"
 
 [package.dependencies]
 python-dateutil = ">=2.7.0"
 
+[[package]]
+name = "backports.entry-points-selectable"
+version = "1.1.0"
+description = "Compatibility shim providing selectable entry points for older implementations"
+category = "main"
+optional = false
+python-versions = ">=2.7"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
+testing = ["pytest (>=4.6)", "pytest-flake8", "pytest-cov", "pytest-black (>=0.3.7)", "pytest-mypy", "pytest-checkdocs (>=2.4)", "pytest-enabler (>=1.0.1)"]
+
 [[package]]
 name = "binaryornot"
 version = "0.4.4"
@@ -28,17 +32,9 @@ python-versions = "*"
 [package.dependencies]
 chardet = ">=3.0.2"
 
-[[package]]
-name = "cached-property"
-version = "1.5.2"
-description = "A decorator for caching properties in classes."
-category = "main"
-optional = false
-python-versions = "*"
-
 [[package]]
 name = "certifi"
-version = "2020.11.8"
+version = "2021.5.30"
 description = "Python package for providing Mozilla's CA Bundle."
 category = "main"
 optional = false
@@ -46,7 +42,7 @@ python-versions = "*"
 
 [[package]]
 name = "cffi"
-version = "1.14.3"
+version = "1.14.6"
 description = "Foreign Function Interface for Python calling C code."
 category = "main"
 optional = false
@@ -57,7 +53,7 @@ pycparser = "*"
 
 [[package]]
 name = "cfgv"
-version = "3.2.0"
+version = "3.3.0"
 description = "Validate configuration and produce human readable error messages."
 category = "main"
 optional = false
@@ -65,23 +61,45 @@ python-versions = ">=3.6.1"
 
 [[package]]
 name = "chardet"
-version = "3.0.4"
+version = "4.0.0"
 description = "Universal encoding detector for Python 2 and 3"
 category = "main"
 optional = false
-python-versions = "*"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+
+[[package]]
+name = "charset-normalizer"
+version = "2.0.4"
+description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
+category = "main"
+optional = false
+python-versions = ">=3.5.0"
+
+[package.extras]
+unicode_backport = ["unicodedata2"]
 
 [[package]]
 name = "click"
-version = "7.1.2"
+version = "8.0.1"
 description = "Composable command line interface toolkit"
 category = "main"
 optional = false
+python-versions = ">=3.6"
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[[package]]
+name = "colorama"
+version = "0.4.4"
+description = "Cross-platform colored terminal text."
+category = "main"
+optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 
 [[package]]
 name = "cookiecutter"
-version = "1.7.2"
+version = "1.7.3"
 description = "A command-line utility that creates projects from project templates, e.g. creating a Python package project from a Python package project template."
 category = "main"
 optional = false
@@ -90,9 +108,8 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
 [package.dependencies]
 binaryornot = ">=0.4.4"
 click = ">=7.0"
-Jinja2 = "<3.0.0"
+Jinja2 = ">=2.7,<4.0.0"
 jinja2-time = ">=0.2.0"
-MarkupSafe = "<2.0.0"
 poyo = ">=0.5.0"
 python-slugify = ">=4.0.0"
 requests = ">=2.23.0"
@@ -100,7 +117,7 @@ six = ">=1.10"
 
 [[package]]
 name = "distlib"
-version = "0.3.1"
+version = "0.3.2"
 description = "Distribution utilities"
 category = "main"
 optional = false
@@ -116,36 +133,36 @@ python-versions = "*"
 
 [[package]]
 name = "identify"
-version = "1.5.9"
+version = "2.2.13"
 description = "File identification library for Python"
 category = "main"
 optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+python-versions = ">=3.6.1"
 
 [package.extras]
-license = ["editdistance"]
+license = ["editdistance-s"]
 
 [[package]]
 name = "idna"
-version = "2.10"
+version = "3.2"
 description = "Internationalized Domain Names in Applications (IDNA)"
 category = "main"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+python-versions = ">=3.5"
 
 [[package]]
 name = "jinja2"
-version = "2.11.2"
+version = "3.0.1"
 description = "A very fast and expressive template engine."
 category = "main"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.6"
 
 [package.dependencies]
-MarkupSafe = ">=0.23"
+MarkupSafe = ">=2.0"
 
 [package.extras]
-i18n = ["Babel (>=0.8)"]
+i18n = ["Babel (>=2.7)"]
 
 [[package]]
 name = "jinja2-time"
@@ -161,20 +178,32 @@ jinja2 = "*"
 
 [[package]]
 name = "markupsafe"
-version = "1.1.1"
+version = "2.0.1"
 description = "Safely add untrusted strings to HTML/XML markup."
 category = "main"
 optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
+python-versions = ">=3.6"
 
 [[package]]
 name = "nodeenv"
-version = "1.5.0"
+version = "1.6.0"
 description = "Node.js virtual environment builder"
 category = "main"
 optional = false
 python-versions = "*"
 
+[[package]]
+name = "platformdirs"
+version = "2.2.0"
+description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+category = "main"
+optional = false
+python-versions = ">=3.6"
+
+[package.extras]
+docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"]
+test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
+
 [[package]]
 name = "poyo"
 version = "0.5.0"
@@ -185,7 +214,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
 [[package]]
 name = "pre-commit"
-version = "2.8.2"
+version = "2.14.0"
 description = "A framework for managing and maintaining multi-language pre-commit hooks."
 category = "main"
 optional = false
@@ -199,6 +228,17 @@ pyyaml = ">=5.1"
 toml = "*"
 virtualenv = ">=20.0.8"
 
+[[package]]
+name = "prompt-toolkit"
+version = "3.0.19"
+description = "Library for building powerful interactive command lines in Python"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+wcwidth = "*"
+
 [[package]]
 name = "pycparser"
 version = "2.20"
@@ -207,21 +247,35 @@ category = "main"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
 
+[[package]]
+name = "pydantic"
+version = "1.8.2"
+description = "Data validation and settings management using python 3.6 type hinting"
+category = "main"
+optional = false
+python-versions = ">=3.6.1"
+
+[package.dependencies]
+typing-extensions = ">=3.7.4.3"
+
+[package.extras]
+dotenv = ["python-dotenv (>=0.10.4)"]
+email = ["email-validator (>=1.0.3)"]
+
 [[package]]
 name = "pygit2"
-version = "1.4.0"
+version = "1.6.1"
 description = "Python bindings for libgit2."
 category = "main"
 optional = false
 python-versions = "*"
 
 [package.dependencies]
-cached-property = "*"
 cffi = ">=1.4.0"
 
 [[package]]
 name = "python-dateutil"
-version = "2.8.1"
+version = "2.8.2"
 description = "Extensions to the standard Python datetime module"
 category = "main"
 optional = false
@@ -232,11 +286,11 @@ six = ">=1.5"
 
 [[package]]
 name = "python-slugify"
-version = "4.0.1"
+version = "5.0.2"
 description = "A Python Slugify application that handles Unicode"
 category = "main"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=3.6"
 
 [package.dependencies]
 text-unidecode = ">=1.3"
@@ -246,33 +300,33 @@ unidecode = ["Unidecode (>=1.1.1)"]
 
 [[package]]
 name = "pyyaml"
-version = "5.3.1"
+version = "5.4.1"
 description = "YAML parser and emitter for Python"
 category = "main"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
 
 [[package]]
 name = "requests"
-version = "2.25.0"
+version = "2.26.0"
 description = "Python HTTP for Humans."
 category = "main"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
 
 [package.dependencies]
 certifi = ">=2017.4.17"
-chardet = ">=3.0.2,<4"
-idna = ">=2.5,<3"
+charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
+idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
 urllib3 = ">=1.21.1,<1.27"
 
 [package.extras]
-security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
 socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
+use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
 
 [[package]]
 name = "six"
-version = "1.15.0"
+version = "1.16.0"
 description = "Python 2 and 3 compatibility utilities"
 category = "main"
 optional = false
@@ -302,9 +356,17 @@ category = "main"
 optional = false
 python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
 
+[[package]]
+name = "typing-extensions"
+version = "3.10.0.0"
+description = "Backported and Experimental Type Hints for Python 3.5+"
+category = "main"
+optional = false
+python-versions = "*"
+
 [[package]]
 name = "urllib3"
-version = "1.26.2"
+version = "1.26.6"
 description = "HTTP library with thread-safe connection pooling, file post, and more."
 category = "main"
 optional = false
@@ -317,223 +379,301 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
 
 [[package]]
 name = "virtualenv"
-version = "20.1.0"
+version = "20.7.2"
 description = "Virtual Python Environment builder"
 category = "main"
 optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
 
 [package.dependencies]
-appdirs = ">=1.4.3,<2"
+"backports.entry-points-selectable" = ">=1.0.4"
 distlib = ">=0.3.1,<1"
 filelock = ">=3.0.0,<4"
+platformdirs = ">=2,<3"
 six = ">=1.9.0,<2"
 
 [package.extras]
 docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)"]
-testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-xdist (>=1.31.0)", "packaging (>=20.0)", "xonsh (>=0.9.16)"]
+testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"]
+
+[[package]]
+name = "wcwidth"
+version = "0.2.5"
+description = "Measures the displayed width of unicode strings in a terminal"
+category = "main"
+optional = false
+python-versions = "*"
 
 [metadata]
 lock-version = "1.1"
 python-versions = "^3.8"
-content-hash = "33b8d26680a3c261f42bc2a403e6fad6800e544608213bf58e35f8aec070e6d6"
+content-hash = "f75e0608a162229e1e7cf160a4adcbbe2e1cb11621073400604d5d5086504c71"
 
 [metadata.files]
-appdirs = [
-    {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
-    {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
-]
 arrow = [
-    {file = "arrow-0.17.0-py2.py3-none-any.whl", hash = "sha256:e098abbd9af3665aea81bdd6c869e93af4feb078e98468dd351c383af187aac5"},
-    {file = "arrow-0.17.0.tar.gz", hash = "sha256:ff08d10cda1d36c68657d6ad20d74fbea493d980f8b2d45344e00d6ed2bf6ed4"},
+    {file = "arrow-1.1.1-py3-none-any.whl", hash = "sha256:77a60a4db5766d900a2085ce9074c5c7b8e2c99afeaa98ad627637ff6f292510"},
+    {file = "arrow-1.1.1.tar.gz", hash = "sha256:dee7602f6c60e3ec510095b5e301441bc56288cb8f51def14dcb3079f623823a"},
+]
+"backports.entry-points-selectable" = [
+    {file = "backports.entry_points_selectable-1.1.0-py2.py3-none-any.whl", hash = "sha256:a6d9a871cde5e15b4c4a53e3d43ba890cc6861ec1332c9c2428c92f977192acc"},
+    {file = "backports.entry_points_selectable-1.1.0.tar.gz", hash = "sha256:988468260ec1c196dab6ae1149260e2f5472c9110334e5d51adcb77867361f6a"},
 ]
 binaryornot = [
     {file = "binaryornot-0.4.4-py2.py3-none-any.whl", hash = "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4"},
     {file = "binaryornot-0.4.4.tar.gz", hash = "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061"},
 ]
-cached-property = [
-    {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"},
-    {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"},
-]
 certifi = [
-    {file = "certifi-2020.11.8-py2.py3-none-any.whl", hash = "sha256:1f422849db327d534e3d0c5f02a263458c3955ec0aae4ff09b95f195c59f4edd"},
-    {file = "certifi-2020.11.8.tar.gz", hash = "sha256:f05def092c44fbf25834a51509ef6e631dc19765ab8a57b4e7ab85531f0a9cf4"},
+    {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
+    {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"},
 ]
 cffi = [
-    {file = "cffi-1.14.3-2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3eeeb0405fd145e714f7633a5173318bd88d8bbfc3dd0a5751f8c4f70ae629bc"},
-    {file = "cffi-1.14.3-2-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:cb763ceceae04803adcc4e2d80d611ef201c73da32d8f2722e9d0ab0c7f10768"},
-    {file = "cffi-1.14.3-2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f60519595eaca110f248e5017363d751b12782a6f2bd6a7041cba275215f5d"},
-    {file = "cffi-1.14.3-2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c53af463f4a40de78c58b8b2710ade243c81cbca641e34debf3396a9640d6ec1"},
-    {file = "cffi-1.14.3-2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:33c6cdc071ba5cd6d96769c8969a0531be2d08c2628a0143a10a7dcffa9719ca"},
-    {file = "cffi-1.14.3-2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c11579638288e53fc94ad60022ff1b67865363e730ee41ad5e6f0a17188b327a"},
-    {file = "cffi-1.14.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3cb3e1b9ec43256c4e0f8d2837267a70b0e1ca8c4f456685508ae6106b1f504c"},
-    {file = "cffi-1.14.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:f0620511387790860b249b9241c2f13c3a80e21a73e0b861a2df24e9d6f56730"},
-    {file = "cffi-1.14.3-cp27-cp27m-win32.whl", hash = "sha256:005f2bfe11b6745d726dbb07ace4d53f057de66e336ff92d61b8c7e9c8f4777d"},
-    {file = "cffi-1.14.3-cp27-cp27m-win_amd64.whl", hash = "sha256:2f9674623ca39c9ebe38afa3da402e9326c245f0f5ceff0623dccdac15023e05"},
-    {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:09e96138280241bd355cd585148dec04dbbedb4f46128f340d696eaafc82dd7b"},
-    {file = "cffi-1.14.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:3363e77a6176afb8823b6e06db78c46dbc4c7813b00a41300a4873b6ba63b171"},
-    {file = "cffi-1.14.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0ef488305fdce2580c8b2708f22d7785ae222d9825d3094ab073e22e93dfe51f"},
-    {file = "cffi-1.14.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:0b1ad452cc824665ddc682400b62c9e4f5b64736a2ba99110712fdee5f2505c4"},
-    {file = "cffi-1.14.3-cp35-cp35m-win32.whl", hash = "sha256:85ba797e1de5b48aa5a8427b6ba62cf69607c18c5d4eb747604b7302f1ec382d"},
-    {file = "cffi-1.14.3-cp35-cp35m-win_amd64.whl", hash = "sha256:e66399cf0fc07de4dce4f588fc25bfe84a6d1285cc544e67987d22663393926d"},
-    {file = "cffi-1.14.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:15f351bed09897fbda218e4db5a3d5c06328862f6198d4fb385f3e14e19decb3"},
-    {file = "cffi-1.14.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4d7c26bfc1ea9f92084a1d75e11999e97b62d63128bcc90c3624d07813c52808"},
-    {file = "cffi-1.14.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:23e5d2040367322824605bc29ae8ee9175200b92cb5483ac7d466927a9b3d537"},
-    {file = "cffi-1.14.3-cp36-cp36m-win32.whl", hash = "sha256:a624fae282e81ad2e4871bdb767e2c914d0539708c0f078b5b355258293c98b0"},
-    {file = "cffi-1.14.3-cp36-cp36m-win_amd64.whl", hash = "sha256:de31b5164d44ef4943db155b3e8e17929707cac1e5bd2f363e67a56e3af4af6e"},
-    {file = "cffi-1.14.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f92cdecb618e5fa4658aeb97d5eb3d2f47aa94ac6477c6daf0f306c5a3b9e6b1"},
-    {file = "cffi-1.14.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:22399ff4870fb4c7ef19fff6eeb20a8bbf15571913c181c78cb361024d574579"},
-    {file = "cffi-1.14.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f4eae045e6ab2bb54ca279733fe4eb85f1effda392666308250714e01907f394"},
-    {file = "cffi-1.14.3-cp37-cp37m-win32.whl", hash = "sha256:b0358e6fefc74a16f745afa366acc89f979040e0cbc4eec55ab26ad1f6a9bfbc"},
-    {file = "cffi-1.14.3-cp37-cp37m-win_amd64.whl", hash = "sha256:6642f15ad963b5092d65aed022d033c77763515fdc07095208f15d3563003869"},
-    {file = "cffi-1.14.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:2791f68edc5749024b4722500e86303a10d342527e1e3bcac47f35fbd25b764e"},
-    {file = "cffi-1.14.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:529c4ed2e10437c205f38f3691a68be66c39197d01062618c55f74294a4a4828"},
-    {file = "cffi-1.14.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f0f1e499e4000c4c347a124fa6a27d37608ced4fe9f7d45070563b7c4c370c9"},
-    {file = "cffi-1.14.3-cp38-cp38-win32.whl", hash = "sha256:3b8eaf915ddc0709779889c472e553f0d3e8b7bdf62dab764c8921b09bf94522"},
-    {file = "cffi-1.14.3-cp38-cp38-win_amd64.whl", hash = "sha256:bbd2f4dfee1079f76943767fce837ade3087b578aeb9f69aec7857d5bf25db15"},
-    {file = "cffi-1.14.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:cc75f58cdaf043fe6a7a6c04b3b5a0e694c6a9e24050967747251fb80d7bce0d"},
-    {file = "cffi-1.14.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:bf39a9e19ce7298f1bd6a9758fa99707e9e5b1ebe5e90f2c3913a47bc548747c"},
-    {file = "cffi-1.14.3-cp39-cp39-win32.whl", hash = "sha256:d80998ed59176e8cba74028762fbd9b9153b9afc71ea118e63bbf5d4d0f9552b"},
-    {file = "cffi-1.14.3-cp39-cp39-win_amd64.whl", hash = "sha256:c150eaa3dadbb2b5339675b88d4573c1be3cb6f2c33a6c83387e10cc0bf05bd3"},
-    {file = "cffi-1.14.3.tar.gz", hash = "sha256:f92f789e4f9241cd262ad7a555ca2c648a98178a953af117ef7fad46aa1d5591"},
+    {file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"},
+    {file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"},
+    {file = "cffi-1.14.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819"},
+    {file = "cffi-1.14.6-cp27-cp27m-win32.whl", hash = "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20"},
+    {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"},
+    {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"},
+    {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"},
+    {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"},
+    {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"},
+    {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"},
+    {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"},
+    {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"},
+    {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"},
+    {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"},
+    {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"},
+    {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d"},
+    {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b"},
+    {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb"},
+    {file = "cffi-1.14.6-cp36-cp36m-win32.whl", hash = "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a"},
+    {file = "cffi-1.14.6-cp36-cp36m-win_amd64.whl", hash = "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e"},
+    {file = "cffi-1.14.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5"},
+    {file = "cffi-1.14.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf"},
+    {file = "cffi-1.14.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"},
+    {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56"},
+    {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c"},
+    {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762"},
+    {file = "cffi-1.14.6-cp37-cp37m-win32.whl", hash = "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771"},
+    {file = "cffi-1.14.6-cp37-cp37m-win_amd64.whl", hash = "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a"},
+    {file = "cffi-1.14.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0"},
+    {file = "cffi-1.14.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e"},
+    {file = "cffi-1.14.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346"},
+    {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc"},
+    {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd"},
+    {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc"},
+    {file = "cffi-1.14.6-cp38-cp38-win32.whl", hash = "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548"},
+    {file = "cffi-1.14.6-cp38-cp38-win_amd64.whl", hash = "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156"},
+    {file = "cffi-1.14.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d"},
+    {file = "cffi-1.14.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e"},
+    {file = "cffi-1.14.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c"},
+    {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202"},
+    {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f"},
+    {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87"},
+    {file = "cffi-1.14.6-cp39-cp39-win32.whl", hash = "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728"},
+    {file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"},
+    {file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"},
 ]
 cfgv = [
-    {file = "cfgv-3.2.0-py2.py3-none-any.whl", hash = "sha256:32e43d604bbe7896fe7c248a9c2276447dbef840feb28fe20494f62af110211d"},
-    {file = "cfgv-3.2.0.tar.gz", hash = "sha256:cf22deb93d4bcf92f345a5c3cd39d3d41d6340adc60c78bbbd6588c384fda6a1"},
+    {file = "cfgv-3.3.0-py2.py3-none-any.whl", hash = "sha256:b449c9c6118fe8cca7fa5e00b9ec60ba08145d281d52164230a69211c5d597a1"},
+    {file = "cfgv-3.3.0.tar.gz", hash = "sha256:9e600479b3b99e8af981ecdfc80a0296104ee610cab48a5ae4ffd0b668650eb1"},
 ]
 chardet = [
-    {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
-    {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"},
+    {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
+    {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
+]
+charset-normalizer = [
+    {file = "charset-normalizer-2.0.4.tar.gz", hash = "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3"},
+    {file = "charset_normalizer-2.0.4-py3-none-any.whl", hash = "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b"},
 ]
 click = [
-    {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
-    {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
+    {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
+    {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
+]
+colorama = [
+    {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
+    {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
 ]
 cookiecutter = [
-    {file = "cookiecutter-1.7.2-py2.py3-none-any.whl", hash = "sha256:430eb882d028afb6102c084bab6cf41f6559a77ce9b18dc6802e3bc0cc5f4a30"},
-    {file = "cookiecutter-1.7.2.tar.gz", hash = "sha256:efb6b2d4780feda8908a873e38f0e61778c23f6a2ea58215723bcceb5b515dac"},
+    {file = "cookiecutter-1.7.3-py2.py3-none-any.whl", hash = "sha256:f8671531fa96ab14339d0c59b4f662a4f12a2ecacd94a0f70a3500843da588e2"},
+    {file = "cookiecutter-1.7.3.tar.gz", hash = "sha256:6b9a4d72882e243be077a7397d0f1f76fe66cf3df91f3115dbb5330e214fa457"},
 ]
 distlib = [
-    {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
-    {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"},
+    {file = "distlib-0.3.2-py2.py3-none-any.whl", hash = "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"},
+    {file = "distlib-0.3.2.zip", hash = "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736"},
 ]
 filelock = [
     {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"},
     {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
 ]
 identify = [
-    {file = "identify-1.5.9-py2.py3-none-any.whl", hash = "sha256:5dd84ac64a9a115b8e0b27d1756b244b882ad264c3c423f42af8235a6e71ca12"},
-    {file = "identify-1.5.9.tar.gz", hash = "sha256:c9504ba6a043ee2db0a9d69e43246bc138034895f6338d5aed1b41e4a73b1513"},
+    {file = "identify-2.2.13-py2.py3-none-any.whl", hash = "sha256:7199679b5be13a6b40e6e19ea473e789b11b4e3b60986499b1f589ffb03c217c"},
+    {file = "identify-2.2.13.tar.gz", hash = "sha256:7bc6e829392bd017236531963d2d937d66fc27cadc643ac0aba2ce9f26157c79"},
 ]
 idna = [
-    {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
-    {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
+    {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"},
+    {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"},
 ]
 jinja2 = [
-    {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
-    {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
+    {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"},
+    {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"},
 ]
 jinja2-time = [
     {file = "jinja2-time-0.2.0.tar.gz", hash = "sha256:d14eaa4d315e7688daa4969f616f226614350c48730bfa1692d2caebd8c90d40"},
     {file = "jinja2_time-0.2.0-py2.py3-none-any.whl", hash = "sha256:d3eab6605e3ec8b7a0863df09cc1d23714908fa61aa6986a845c20ba488b4efa"},
 ]
 markupsafe = [
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"},
-    {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
+    {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
 ]
 nodeenv = [
-    {file = "nodeenv-1.5.0-py2.py3-none-any.whl", hash = "sha256:5304d424c529c997bc888453aeaa6362d242b6b4631e90f3d4bf1b290f1c84a9"},
-    {file = "nodeenv-1.5.0.tar.gz", hash = "sha256:ab45090ae383b716c4ef89e690c41ff8c2b257b85b309f01f3654df3d084bd7c"},
+    {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"},
+    {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"},
+]
+platformdirs = [
+    {file = "platformdirs-2.2.0-py3-none-any.whl", hash = "sha256:4666d822218db6a262bdfdc9c39d21f23b4cfdb08af331a81e92751daf6c866c"},
+    {file = "platformdirs-2.2.0.tar.gz", hash = "sha256:632daad3ab546bd8e6af0537d09805cec458dce201bccfe23012df73332e181e"},
 ]
 poyo = [
     {file = "poyo-0.5.0-py2.py3-none-any.whl", hash = "sha256:3e2ca8e33fdc3c411cd101ca395668395dd5dc7ac775b8e809e3def9f9fe041a"},
     {file = "poyo-0.5.0.tar.gz", hash = "sha256:e26956aa780c45f011ca9886f044590e2d8fd8b61db7b1c1cf4e0869f48ed4dd"},
 ]
 pre-commit = [
-    {file = "pre_commit-2.8.2-py2.py3-none-any.whl", hash = "sha256:22e6aa3bd571debb01eb7d34483f11c01b65237be4eebbf30c3d4fb65762d315"},
-    {file = "pre_commit-2.8.2.tar.gz", hash = "sha256:905ebc9b534b991baec87e934431f2d0606ba27f2b90f7f652985f5a5b8b6ae6"},
+    {file = "pre_commit-2.14.0-py2.py3-none-any.whl", hash = "sha256:ec3045ae62e1aa2eecfb8e86fa3025c2e3698f77394ef8d2011ce0aedd85b2d4"},
+    {file = "pre_commit-2.14.0.tar.gz", hash = "sha256:2386eeb4cf6633712c7cc9ede83684d53c8cafca6b59f79c738098b51c6d206c"},
+]
+prompt-toolkit = [
+    {file = "prompt_toolkit-3.0.19-py3-none-any.whl", hash = "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88"},
+    {file = "prompt_toolkit-3.0.19.tar.gz", hash = "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f"},
 ]
 pycparser = [
     {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
     {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
 ]
+pydantic = [
+    {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"},
+    {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"},
+    {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"},
+    {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"},
+    {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"},
+    {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"},
+    {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"},
+    {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"},
+    {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"},
+    {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"},
+    {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"},
+    {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"},
+    {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"},
+    {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"},
+    {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"},
+    {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"},
+    {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"},
+    {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"},
+    {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"},
+    {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"},
+    {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"},
+    {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"},
+]
 pygit2 = [
-    {file = "pygit2-1.4.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b422e417739def0a136a6355723dfe8a5ffc83db5098076f28a14f1d139779c1"},
-    {file = "pygit2-1.4.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32eb863d6651d4890ced318505ea8dc229bd9637deaf29c898de1ab574d727a0"},
-    {file = "pygit2-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:0d298098e286eeda000e49ca7e1b41f87300e10dd8b9d06b32b008bd61f50b83"},
-    {file = "pygit2-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:9c8d5881eb709e2e2e13000b507a131bd5fb91a879581030088d0ddffbcd19af"},
-    {file = "pygit2-1.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ddb7a1f6d38063e8724abfa1cfdfb0f9b25014b8bca0546274b7a84b873a3888"},
-    {file = "pygit2-1.4.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ec5c0365a9bdfcac1609d20868507b28685ec5ea7cc3a2c903c9b62ef2e0bbc0"},
-    {file = "pygit2-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:818c91b582109d90580c5da74af783738838353f15eb12eeb734d80a974b05a3"},
-    {file = "pygit2-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9c2f2d9ef59513007b66f6534b000792b614de3faf60313a0a68f6b8571aea85"},
-    {file = "pygit2-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8306a302487dac67df7af6a064bb37e8a8eb4138958f9560ff49ff162e185dab"},
-    {file = "pygit2-1.4.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d0d889144e9487d926fecea947c3f39ce5f477e521d7d467d2e66907e4cd657d"},
-    {file = "pygit2-1.4.0-cp38-cp38-win32.whl", hash = "sha256:41204b6f3406d9f53147710f3cc485d77181ba67f57c34d36b7c86de1c14a18c"},
-    {file = "pygit2-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:cf00481ddf053e549a6edd0216bdc267b292d261eae02a67bb3737de920cbf88"},
-    {file = "pygit2-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:37d6d7d6d7804c42a0fe23425c72e38093488525092fc5e51a05684e63503ce7"},
-    {file = "pygit2-1.4.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:e9037a7d810750fe23c9f5641ef14a0af2525ff03e14752cd4f73e1870ecfcb0"},
-    {file = "pygit2-1.4.0-cp39-cp39-win32.whl", hash = "sha256:fdd8ba30cda277290e000322f505132f590cf89bd7d31829b45a3cb57447ec32"},
-    {file = "pygit2-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ee135eb2cd8b07ce1374f3596cc5c3213472d6389bad6a4c5d87d8e267e93e9"},
-    {file = "pygit2-1.4.0.tar.gz", hash = "sha256:cbeb38ab1df9b5d8896548a11e63aae8a064763ab5f1eabe4475e6b8a78ee1c8"},
+    {file = "pygit2-1.6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:547429774c11f5bc9d20a49aa86e4bd13c90a55140504ef05f55cf424470ee34"},
+    {file = "pygit2-1.6.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e75865d7b6fc161d93b16f10365eaad353cd546e302a98f2de2097ddea1066b"},
+    {file = "pygit2-1.6.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4a64b6090308ffd1c82e2dd4316cb79483715387b13818156d516134a5b17c"},
+    {file = "pygit2-1.6.1-cp36-cp36m-win32.whl", hash = "sha256:2666a3970b2ea1222a9f0463b466f98c8d564f29ec84cf0a58d9b0d3865dbaaf"},
+    {file = "pygit2-1.6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2de12ca2d3b7eb86106223b40b2edc0c61103c71e7962e53092c6ddef71a194"},
+    {file = "pygit2-1.6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:9c1d96c66fb6e69ec710078a73c19edff420bc1db430caa9e03a825eede3f25c"},
+    {file = "pygit2-1.6.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:454d42550fa6a6cd0e6a6ad9ab3f3262135fd157f57bad245ce156c36ee93370"},
+    {file = "pygit2-1.6.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce0827b77dd2f8a3465bdc181c4e65f27dd12dbd92635c038e58030cc90c2de0"},
+    {file = "pygit2-1.6.1-cp37-cp37m-win32.whl", hash = "sha256:b0161a141888d450eb821472fdcdadd14a072ddeda841fee9984956d34d3e19d"},
+    {file = "pygit2-1.6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:af2fa259b6f7899227611ab978c600695724e85965836cb607d8b1e70cfea9b3"},
+    {file = "pygit2-1.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0e1e02c28983ddc004c0f54063f3e46fca388225d468e32e16689cfb750e0bd6"},
+    {file = "pygit2-1.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5dadc4844feb76cde5cc9a37656326a361dd8b5c8e8f8674dcd4a5ecf395db3"},
+    {file = "pygit2-1.6.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef07458e4172a31318663295083b43f957d611145738ff56aa76db593542a6e8"},
+    {file = "pygit2-1.6.1-cp38-cp38-win32.whl", hash = "sha256:7a0c0a1f11fd41f57e8c6c64d903cc7fa4ec95d15592270be3217ed7f78eb023"},
+    {file = "pygit2-1.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:2fd5c1b2d84dc6084f1bda836607afe37e95186a53a5a827a69083415e57fe4f"},
+    {file = "pygit2-1.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b9b88b7e9a5286a71be0b6c307f0523c9606aeedff6b61eb9c440e18817fa641"},
+    {file = "pygit2-1.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac12d32b714c3383ebccffee5eb6aff0b69a2542a40a664fd5ad370afcb28ee7"},
+    {file = "pygit2-1.6.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe682ed6afd2ab31127f6a502cf3e002dc1cc8d26c36a5d49dfd180250351eb6"},
+    {file = "pygit2-1.6.1-cp39-cp39-win32.whl", hash = "sha256:dbbf66a23860aa899949068ac9b503b4bc21e6063e8f53870440adbdc909405e"},
+    {file = "pygit2-1.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:f90775afb11f69376e2af21ab56fcfbb52f6bc84117059ddf0355f81e5e36352"},
+    {file = "pygit2-1.6.1.tar.gz", hash = "sha256:c3303776f774d3e0115c1c4f6e1fc35470d15f113a7ae9401a0b90acfa1661ac"},
 ]
 python-dateutil = [
-    {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"},
-    {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"},
+    {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
+    {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
 ]
 python-slugify = [
-    {file = "python-slugify-4.0.1.tar.gz", hash = "sha256:69a517766e00c1268e5bbfc0d010a0a8508de0b18d30ad5a1ff357f8ae724270"},
+    {file = "python-slugify-5.0.2.tar.gz", hash = "sha256:f13383a0b9fcbe649a1892b9c8eb4f8eab1d6d84b84bb7a624317afa98159cab"},
+    {file = "python_slugify-5.0.2-py2.py3-none-any.whl", hash = "sha256:6d8c5df75cd4a7c3a2d21e257633de53f52ab0265cd2d1dc62a730e8194a7380"},
 ]
 pyyaml = [
-    {file = "PyYAML-5.3.1-cp27-cp27m-win32.whl", hash = "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f"},
-    {file = "PyYAML-5.3.1-cp27-cp27m-win_amd64.whl", hash = "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76"},
-    {file = "PyYAML-5.3.1-cp35-cp35m-win32.whl", hash = "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2"},
-    {file = "PyYAML-5.3.1-cp35-cp35m-win_amd64.whl", hash = "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c"},
-    {file = "PyYAML-5.3.1-cp36-cp36m-win32.whl", hash = "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2"},
-    {file = "PyYAML-5.3.1-cp36-cp36m-win_amd64.whl", hash = "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648"},
-    {file = "PyYAML-5.3.1-cp37-cp37m-win32.whl", hash = "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a"},
-    {file = "PyYAML-5.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf"},
-    {file = "PyYAML-5.3.1-cp38-cp38-win32.whl", hash = "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97"},
-    {file = "PyYAML-5.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee"},
-    {file = "PyYAML-5.3.1.tar.gz", hash = "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d"},
+    {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
+    {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"},
+    {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"},
+    {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"},
+    {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"},
+    {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"},
+    {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"},
+    {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"},
+    {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"},
+    {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"},
+    {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"},
+    {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"},
+    {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"},
+    {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"},
+    {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"},
+    {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"},
+    {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"},
+    {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"},
+    {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"},
+    {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"},
+    {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"},
+    {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"},
+    {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"},
+    {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"},
+    {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"},
+    {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"},
+    {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"},
+    {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
+    {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
 ]
 requests = [
-    {file = "requests-2.25.0-py2.py3-none-any.whl", hash = "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"},
-    {file = "requests-2.25.0.tar.gz", hash = "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8"},
+    {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"},
+    {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"},
 ]
 six = [
-    {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
-    {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
+    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
 ]
 termcolor = [
     {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"},
@@ -546,11 +686,20 @@ toml = [
     {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
     {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
 ]
+typing-extensions = [
+    {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"},
+    {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"},
+    {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"},
+]
 urllib3 = [
-    {file = "urllib3-1.26.2-py2.py3-none-any.whl", hash = "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"},
-    {file = "urllib3-1.26.2.tar.gz", hash = "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08"},
+    {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"},
+    {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"},
 ]
 virtualenv = [
-    {file = "virtualenv-20.1.0-py2.py3-none-any.whl", hash = "sha256:b0011228208944ce71052987437d3843e05690b2f23d1c7da4263fde104c97a2"},
-    {file = "virtualenv-20.1.0.tar.gz", hash = "sha256:b8d6110f493af256a40d65e29846c69340a947669eec8ce784fcf3dd3af28380"},
+    {file = "virtualenv-20.7.2-py2.py3-none-any.whl", hash = "sha256:e4670891b3a03eb071748c569a87cceaefbf643c5bac46d996c5a45c34aa0f06"},
+    {file = "virtualenv-20.7.2.tar.gz", hash = "sha256:9ef4e8ee4710826e98ff3075c9a4739e2cb1040de6a2a8d35db0055840dc96a0"},
+]
+wcwidth = [
+    {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
+    {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
 ]
diff --git a/pyproject.toml b/pyproject.toml
index e2b0738..03035b1 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "fastapi_template"
-version = "1.2.3"
+version = "2.0.0"
 description = "Feature-rich robust FastAPI template"
 authors = ["Pavel Kirilin <win10@list.ru>"]
 packages = [
@@ -18,15 +18,17 @@ keywords = [
 
 [tool.poetry.dependencies]
 python = "^3.8"
-cookiecutter = "^1.7.2"
-pre-commit = "^2.8.2"
+cookiecutter = "^1.7.3"
+pre-commit = "^2.14.0"
 termcolor = "^1.1.0"
-pygit2 = "^1.4.0"
+pygit2 = "^1.6.0"
+pydantic = "^1.8.2"
+prompt-toolkit = "^3.0.19"
 
 [tool.poetry.dev-dependencies]
 
 [tool.poetry.scripts]
-fastapi_template = "fastapi_template.main:main"
+fastapi_template = "fastapi_template.__main__:main"
 
 [build-system]
 requires = ["poetry-core>=1.0.0"]
-- 
GitLab