├── LICENSE ├── config ├── __init__.py ├── settings │ ├── __init__.py │ ├── test.py │ ├── local.py │ ├── production.py │ └── base.py ├── wsgi.py └── urls.py ├── demoapp ├── users │ ├── __init__.py │ ├── tests │ │ ├── __init__.py │ │ ├── factories.py │ │ ├── test_models.py │ │ ├── test_admin.py │ │ ├── test_urls.py │ │ └── test_views.py │ ├── migrations │ │ ├── __init__.py │ │ └── 0001_initial.py │ ├── apps.py │ ├── urls.py │ ├── adapters.py │ ├── models.py │ ├── admin.py │ └── views.py ├── static │ ├── fonts │ │ └── .gitkeep │ ├── sass │ │ ├── custom_bootstrap_vars.scss │ │ └── project.scss │ ├── images │ │ └── favicons │ │ │ └── favicon.ico │ ├── css │ │ └── project.css │ └── js │ │ └── project.js ├── templates │ ├── pages │ │ ├── home.html │ │ └── about.html │ ├── 404.html │ ├── 403_csrf.html │ ├── account │ │ ├── base.html │ │ ├── account_inactive.html │ │ ├── password_reset_from_key_done.html │ │ ├── signup_closed.html │ │ ├── verification_sent.html │ │ ├── password_set.html │ │ ├── password_reset_done.html │ │ ├── password_change.html │ │ ├── logout.html │ │ ├── signup.html │ │ ├── verified_email_required.html │ │ ├── password_reset.html │ │ ├── password_reset_from_key.html │ │ ├── email_confirm.html │ │ ├── login.html │ │ └── email.html │ ├── 500.html │ ├── users │ │ ├── user_list.html │ │ ├── user_form.html │ │ └── user_detail.html │ └── base.html ├── __init__.py └── contrib │ ├── __init__.py │ └── sites │ ├── __init__.py │ └── migrations │ ├── __init__.py │ ├── 0002_alter_domain_unique.py │ ├── 0003_set_site_domain_and_name.py │ └── 0001_initial.py ├── .gitattributes ├── CONTRIBUTORS.txt ├── superset-conf ├── config │ ├── __init__.py │ ├── security.py │ └── superset_config.py ├── data │ └── .gitignore └── custom-css-templates │ ├── blue-dashboard.css │ └── red-dashboard.css ├── nginx-conf ├── mysite.template ├── default.conf └── nginx.conf ├── start.sh ├── stop.sh ├── logs-view.sh ├── .dockerignore ├── pytest.ini ├── docs ├── __init__.py ├── deploy.rst ├── install.rst ├── index.rst ├── make.bat ├── Makefile ├── docker_ec2.rst └── conf.py ├── .coveragerc ├── compose ├── production │ ├── caddy │ │ ├── Dockerfile │ │ └── Caddyfile │ ├── postgres │ │ ├── maintenance │ │ │ ├── _sourced │ │ │ │ ├── constants.sh │ │ │ │ ├── yes_no.sh │ │ │ │ ├── countdown.sh │ │ │ │ └── messages.sh │ │ │ ├── backups │ │ │ ├── backup │ │ │ └── restore │ │ └── Dockerfile │ └── django │ │ ├── start │ │ ├── entrypoint │ │ └── Dockerfile └── local │ └── django │ ├── start │ └── Dockerfile ├── locale └── README.rst ├── setup.cfg ├── .envs └── .local │ ├── .postgres │ └── .django ├── .pylintrc ├── .project ├── utility ├── requirements-trusty.apt ├── requirements-xenial.apt ├── requirements-jessie.apt ├── requirements-stretch.apt ├── install_python_dependencies.sh └── install_os_dependencies.sh ├── requirements ├── production.txt ├── base.txt └── local.txt ├── .editorconfig ├── DummyData.md ├── .pydevproject ├── manage.py ├── production.yml ├── local.yml ├── merge_production_dotenvs_in_dotenv.py ├── README.md └── .gitignore /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/settings/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demoapp/users/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /CONTRIBUTORS.txt: -------------------------------------------------------------------------------- 1 | Sairam Krish 2 | -------------------------------------------------------------------------------- /demoapp/static/fonts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demoapp/users/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /superset-conf/config/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demoapp/users/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demoapp/static/sass/custom_bootstrap_vars.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nginx-conf/mysite.template: -------------------------------------------------------------------------------- 1 | listen ${NGINX_PORT}; -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | docker-compose -f local.yml up -d 2 | -------------------------------------------------------------------------------- /stop.sh: -------------------------------------------------------------------------------- 1 | docker-compose -f local.yml down 2 | -------------------------------------------------------------------------------- /logs-view.sh: -------------------------------------------------------------------------------- 1 | docker-compose -f local.yml logs -f 2 | -------------------------------------------------------------------------------- /demoapp/templates/pages/home.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} -------------------------------------------------------------------------------- /superset-conf/data/.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | superset.db -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .* 2 | !.coveragerc 3 | !.env 4 | !.pylintrc 5 | -------------------------------------------------------------------------------- /demoapp/templates/pages/about.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | DJANGO_SETTINGS_MODULE=config.settings.test 3 | -------------------------------------------------------------------------------- /docs/__init__.py: -------------------------------------------------------------------------------- 1 | # Included so that Django's startproject comment runs against the docs directory 2 | -------------------------------------------------------------------------------- /docs/deploy.rst: -------------------------------------------------------------------------------- 1 | Deploy 2 | ======== 3 | 4 | This is where you describe how the project is deployed in production. 5 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | include = demoapp/* 3 | omit = *migrations*, *tests* 4 | plugins = 5 | django_coverage_plugin 6 | -------------------------------------------------------------------------------- /docs/install.rst: -------------------------------------------------------------------------------- 1 | Install 2 | ========= 3 | 4 | This is where you write how to get a new laptop to run this project. 5 | -------------------------------------------------------------------------------- /compose/production/caddy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM abiosoft/caddy:0.11.0 2 | 3 | COPY ./compose/production/caddy/Caddyfile /etc/Caddyfile 4 | -------------------------------------------------------------------------------- /demoapp/static/images/favicons/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sairamkrish/django-demo-project/HEAD/demoapp/static/images/favicons/favicon.ico -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/_sourced/constants.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | BACKUP_DIR_PATH='/backups' 5 | BACKUP_FILE_PREFIX='backup' 6 | -------------------------------------------------------------------------------- /locale/README.rst: -------------------------------------------------------------------------------- 1 | Translations 2 | ============ 3 | 4 | Translations will be placed in this folder when running:: 5 | 6 | python manage.py makemessages 7 | -------------------------------------------------------------------------------- /compose/local/django/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | python manage.py migrate 9 | python manage.py runserver_plus 0.0.0.0:8000 10 | -------------------------------------------------------------------------------- /demoapp/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.0" 2 | __version_info__ = tuple( 3 | [ 4 | int(num) if num.isdigit() else num 5 | for num in __version__.replace("-", ".", 1).split(".") 6 | ] 7 | ) 8 | -------------------------------------------------------------------------------- /demoapp/contrib/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /demoapp/contrib/sites/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length = 120 3 | exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules 4 | 5 | [pycodestyle] 6 | max-line-length = 120 7 | exclude = .tox,.git,*/migrations/*,*/static/CACHE/*,docs,node_modules 8 | -------------------------------------------------------------------------------- /.envs/.local/.postgres: -------------------------------------------------------------------------------- 1 | # PostgreSQL 2 | # ------------------------------------------------------------------------------ 3 | POSTGRES_HOST=postgres 4 | POSTGRES_PORT=5432 5 | POSTGRES_DB=demoapp 6 | POSTGRES_USER=debug 7 | POSTGRES_PASSWORD=debug 8 | -------------------------------------------------------------------------------- /compose/production/django/start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | python /app/manage.py collectstatic --noinput 9 | /usr/local/bin/gunicorn config.wsgi --bind 0.0.0.0:5000 --chdir=/app 10 | -------------------------------------------------------------------------------- /demoapp/contrib/sites/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | -------------------------------------------------------------------------------- /demoapp/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Page not found{% endblock %} 4 | 5 | {% block content %} 6 |

Page not found

7 | 8 |

This is not the page you were looking for.

9 | {% endblock content %} 10 | -------------------------------------------------------------------------------- /demoapp/templates/403_csrf.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Forbidden (403){% endblock %} 4 | 5 | {% block content %} 6 |

Forbidden (403)

7 | 8 |

CSRF verification failed. Request aborted.

9 | {% endblock content %} 10 | -------------------------------------------------------------------------------- /.envs/.local/.django: -------------------------------------------------------------------------------- 1 | # General 2 | # ------------------------------------------------------------------------------ 3 | USE_DOCKER=yes 4 | 5 | # Redis 6 | # ------------------------------------------------------------------------------ 7 | REDIS_URL=redis://redis:6379/0 8 | -------------------------------------------------------------------------------- /compose/production/postgres/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM postgres:10-alpine 2 | 3 | COPY ./compose/production/postgres/maintenance /usr/local/bin/maintenance 4 | RUN chmod +x /usr/local/bin/maintenance/* 5 | RUN mv /usr/local/bin/maintenance/* /usr/local/bin \ 6 | && rmdir /usr/local/bin/maintenance 7 | -------------------------------------------------------------------------------- /demoapp/static/css/project.css: -------------------------------------------------------------------------------- 1 | /* These styles are generated from project.scss. */ 2 | 3 | .alert-debug { 4 | color: black; 5 | background-color: white; 6 | border-color: #d6e9c6; 7 | } 8 | 9 | .alert-error { 10 | color: #b94a48; 11 | background-color: #f2dede; 12 | border-color: #eed3d7; 13 | } 14 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | load-plugins=pylint_common, pylint_django 3 | 4 | [FORMAT] 5 | max-line-length=120 6 | 7 | [MESSAGES CONTROL] 8 | disable=missing-docstring,invalid-name 9 | 10 | [DESIGN] 11 | max-parents=13 12 | 13 | [TYPECHECK] 14 | generated-members=REQUEST,acl_users,aq_parent,"[a-zA-Z]+_set{1,2}",save,delete 15 | -------------------------------------------------------------------------------- /demoapp/templates/account/base.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% block title %}{% block head_title %}{% endblock head_title %}{% endblock title %} 3 | 4 | {% block content %} 5 |
6 |
7 | {% block inner %}{% endblock %} 8 |
9 |
10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /demoapp/templates/account/account_inactive.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Account Inactive" %}{% endblock %} 6 | 7 | {% block inner %} 8 |

{% trans "Account Inactive" %}

9 | 10 |

{% trans "This account is inactive." %}

11 | {% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /demoapp/templates/account/password_reset_from_key_done.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% block head_title %}{% trans "Change Password" %}{% endblock %} 5 | 6 | {% block inner %} 7 |

{% trans "Change Password" %}

8 |

{% trans 'Your password is now changed.' %}

9 | {% endblock %} 10 | 11 | -------------------------------------------------------------------------------- /demoapp/templates/account/signup_closed.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Sign Up Closed" %}{% endblock %} 6 | 7 | {% block inner %} 8 |

{% trans "Sign Up Closed" %}

9 | 10 |

{% trans "We are sorry, but the sign up is currently closed." %}

11 | {% endblock %} 12 | 13 | -------------------------------------------------------------------------------- /compose/production/caddy/Caddyfile: -------------------------------------------------------------------------------- 1 | www.{$DOMAIN_NAME} { 2 | redir https://example.com 3 | } 4 | 5 | {$DOMAIN_NAME} { 6 | proxy / django:5000 { 7 | header_upstream Host {host} 8 | header_upstream X-Real-IP {remote} 9 | header_upstream X-Forwarded-Proto {scheme} 10 | } 11 | log stdout 12 | errors stdout 13 | gzip 14 | } 15 | -------------------------------------------------------------------------------- /demoapp/templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Server Error{% endblock %} 4 | 5 | {% block content %} 6 |

Ooops!!! 500

7 | 8 |

Looks like something went wrong!

9 | 10 |

We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, try refreshing.

11 | {% endblock content %} 12 | 13 | 14 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/_sourced/yes_no.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | yes_no() { 5 | declare desc="Prompt for confirmation. \$\"\{1\}\": confirmation message." 6 | local arg1="${1}" 7 | 8 | local response= 9 | read -r -p "${arg1} (y/[n])? " response 10 | if [[ "${response}" =~ ^[Yy]$ ]] 11 | then 12 | exit 0 13 | else 14 | exit 1 15 | fi 16 | } 17 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/_sourced/countdown.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | countdown() { 5 | declare desc="A simple countdown. Source: https://superuser.com/a/611582" 6 | local seconds="${1}" 7 | local d=$(($(date +%s) + "${seconds}")) 8 | while [ "$d" -ge `date +%s` ]; do 9 | echo -ne "$(date -u --date @$(($d - `date +%s`)) +%H:%M:%S)\r"; 10 | sleep 0.1 11 | done 12 | } 13 | -------------------------------------------------------------------------------- /demoapp/users/tests/factories.py: -------------------------------------------------------------------------------- 1 | import factory 2 | 3 | 4 | class UserFactory(factory.django.DjangoModelFactory): 5 | username = factory.Sequence(lambda n: f"user-{n}") 6 | email = factory.Sequence(lambda n: f"user-{n}@example.com") 7 | password = factory.PostGenerationMethodCall("set_password", "password") 8 | 9 | class Meta: 10 | model = "users.User" 11 | django_get_or_create = ("username",) 12 | -------------------------------------------------------------------------------- /demoapp/users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UsersAppConfig(AppConfig): 5 | name = "demoapp.users" 6 | verbose_name = "Users" 7 | 8 | def ready(self): 9 | """Override this to put in: 10 | Users system checks 11 | Users signal registration 12 | """ 13 | try: 14 | import users.signals # noqa F401 15 | except ImportError: 16 | pass 17 | -------------------------------------------------------------------------------- /demoapp/users/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = "users" 6 | urlpatterns = [ 7 | path("", view=views.UserListView.as_view(), name="list"), 8 | path("~redirect/", view=views.UserRedirectView.as_view(), name="redirect"), 9 | path("~update/", view=views.UserUpdateView.as_view(), name="update"), 10 | path( 11 | "/", 12 | view=views.UserDetailView.as_view(), 13 | name="detail", 14 | ), 15 | ] 16 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | django-demo-project 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.python.pydev.pythonNature 16 | org.python.pydev.django.djangoNature 17 | 18 | 19 | -------------------------------------------------------------------------------- /demoapp/users/tests/test_models.py: -------------------------------------------------------------------------------- 1 | from test_plus.test import TestCase 2 | 3 | 4 | class TestUser(TestCase): 5 | 6 | def setUp(self): 7 | self.user = self.make_user() 8 | 9 | def test__str__(self): 10 | self.assertEqual( 11 | self.user.__str__(), 12 | "testuser", # This is the default username for self.make_user() 13 | ) 14 | 15 | def test_get_absolute_url(self): 16 | self.assertEqual(self.user.get_absolute_url(), "/users/testuser/") 17 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/backups: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | ### View backups. 5 | ### 6 | ### Usage: 7 | ### $ docker-compose -f .yml (exec |run --rm) postgres backups 8 | 9 | 10 | set -o errexit 11 | set -o pipefail 12 | set -o nounset 13 | 14 | 15 | working_dir="$(dirname ${0})" 16 | source "${working_dir}/_sourced/constants.sh" 17 | source "${working_dir}/_sourced/messages.sh" 18 | 19 | 20 | message_welcome "These are the backups you have got:" 21 | 22 | ls -lht "${BACKUP_DIR_PATH}" 23 | -------------------------------------------------------------------------------- /demoapp/templates/account/verification_sent.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Verify Your E-mail Address" %}{% endblock %} 6 | 7 | {% block inner %} 8 |

{% trans "Verify Your E-mail Address" %}

9 | 10 |

{% blocktrans %}We have sent an e-mail to you for verification. Follow the link provided to finalize the signup process. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}

11 | 12 | {% endblock %} 13 | 14 | -------------------------------------------------------------------------------- /demoapp/templates/users/user_list.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load static i18n %} 3 | {% block title %}Members{% endblock %} 4 | 5 | {% block content %} 6 |
7 |

Users

8 | 9 |
10 | {% for user in user_list %} 11 | 12 |

{{ user.username }}

13 |
14 | {% endfor %} 15 |
16 |
17 | {% endblock content %} 18 | -------------------------------------------------------------------------------- /utility/requirements-trusty.apt: -------------------------------------------------------------------------------- 1 | ##basic build dependencies of various Django apps for Ubuntu Trusty 14.04 2 | #build-essential metapackage install: make, gcc, g++, 3 | build-essential 4 | #required to translate 5 | gettext 6 | python3-dev 7 | 8 | ##shared dependencies of: 9 | ##Pillow, pylibmc 10 | zlib1g-dev 11 | 12 | ##Postgresql and psycopg2 dependencies 13 | libpq-dev 14 | 15 | ##Pillow dependencies 16 | libtiff4-dev 17 | libjpeg8-dev 18 | libfreetype6-dev 19 | liblcms1-dev 20 | libwebp-dev 21 | 22 | ##django-extensions 23 | graphviz-dev 24 | -------------------------------------------------------------------------------- /utility/requirements-xenial.apt: -------------------------------------------------------------------------------- 1 | ##basic build dependencies of various Django apps for Ubuntu Xenial 16.04 2 | #build-essential metapackage install: make, gcc, g++, 3 | build-essential 4 | #required to translate 5 | gettext 6 | python3-dev 7 | 8 | ##shared dependencies of: 9 | ##Pillow, pylibmc 10 | zlib1g-dev 11 | 12 | ##Postgresql and psycopg2 dependencies 13 | libpq-dev 14 | 15 | ##Pillow dependencies 16 | libtiff5-dev 17 | libjpeg8-dev 18 | libfreetype6-dev 19 | liblcms2-dev 20 | libwebp-dev 21 | 22 | ##django-extensions 23 | graphviz-dev 24 | -------------------------------------------------------------------------------- /utility/requirements-jessie.apt: -------------------------------------------------------------------------------- 1 | ##basic build dependencies of various Django apps for Debian Jessie 8.x 2 | #build-essential metapackage install: make, gcc, g++, 3 | build-essential 4 | #required to translate 5 | gettext 6 | python3-dev 7 | 8 | ##shared dependencies of: 9 | ##Pillow, pylibmc 10 | zlib1g-dev 11 | 12 | ##Postgresql and psycopg2 dependencies 13 | libpq-dev 14 | 15 | ##Pillow dependencies 16 | libtiff5-dev 17 | libjpeg62-turbo-dev 18 | libfreetype6-dev 19 | liblcms2-dev 20 | libwebp-dev 21 | 22 | ##django-extensions 23 | graphviz-dev 24 | -------------------------------------------------------------------------------- /utility/requirements-stretch.apt: -------------------------------------------------------------------------------- 1 | ##basic build dependencies of various Django apps for Debian Jessie 9.x 2 | #build-essential metapackage install: make, gcc, g++, 3 | build-essential 4 | #required to translate 5 | gettext 6 | python3-dev 7 | 8 | ##shared dependencies of: 9 | ##Pillow, pylibmc 10 | zlib1g-dev 11 | 12 | ##Postgresql and psycopg2 dependencies 13 | libpq-dev 14 | 15 | ##Pillow dependencies 16 | libtiff5-dev 17 | libjpeg62-turbo-dev 18 | libfreetype6-dev 19 | liblcms2-dev 20 | libwebp-dev 21 | 22 | ##django-extensions 23 | graphviz-dev 24 | -------------------------------------------------------------------------------- /demoapp/templates/users/user_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block title %}{{ user.username }}{% endblock %} 5 | 6 | {% block content %} 7 |

{{ user.username }}

8 |
9 | {% csrf_token %} 10 | {{ form|crispy }} 11 |
12 |
13 | 14 |
15 |
16 |
17 | {% endblock %} 18 | -------------------------------------------------------------------------------- /demoapp/templates/account/password_set.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block head_title %}{% trans "Set Password" %}{% endblock %} 7 | 8 | {% block inner %} 9 |

{% trans "Set Password" %}

10 | 11 |
12 | {% csrf_token %} 13 | {{ form|crispy }} 14 | 15 |
16 | {% endblock %} 17 | 18 | -------------------------------------------------------------------------------- /demoapp/users/adapters.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from allauth.account.adapter import DefaultAccountAdapter 3 | from allauth.socialaccount.adapter import DefaultSocialAccountAdapter 4 | 5 | 6 | class AccountAdapter(DefaultAccountAdapter): 7 | 8 | def is_open_for_signup(self, request): 9 | return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True) 10 | 11 | 12 | class SocialAccountAdapter(DefaultSocialAccountAdapter): 13 | 14 | def is_open_for_signup(self, request, sociallogin): 15 | return getattr(settings, "ACCOUNT_ALLOW_REGISTRATION", True) 16 | -------------------------------------------------------------------------------- /demoapp/templates/account/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load account %} 5 | 6 | {% block head_title %}{% trans "Password Reset" %}{% endblock %} 7 | 8 | {% block inner %} 9 |

{% trans "Password Reset" %}

10 | 11 | {% if user.is_authenticated %} 12 | {% include "account/snippets/already_logged_in.html" %} 13 | {% endif %} 14 | 15 |

{% blocktrans %}We have sent you an e-mail. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}

16 | {% endblock %} 17 | 18 | -------------------------------------------------------------------------------- /requirements/production.txt: -------------------------------------------------------------------------------- 1 | # PRECAUTION: avoid production dependencies that aren't in development 2 | 3 | -r ./base.txt 4 | 5 | gunicorn==19.8.1 # https://github.com/benoitc/gunicorn 6 | psycopg2==2.7.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 7 | Collectfast==0.6.2 # https://github.com/antonagestam/collectfast 8 | 9 | # Django 10 | # ------------------------------------------------------------------------------ 11 | django-storages[boto3]==1.6.6 # https://github.com/jschneier/django-storages 12 | django-anymail[mailgun]==3.0 # https://github.com/anymail/django-anymail 13 | -------------------------------------------------------------------------------- /demoapp/templates/account/password_change.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block head_title %}{% trans "Change Password" %}{% endblock %} 7 | 8 | {% block inner %} 9 |

{% trans "Change Password" %}

10 | 11 |
12 | {% csrf_token %} 13 | {{ form|crispy }} 14 | 15 |
16 | {% endblock %} 17 | 18 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. DemoApp documentation master file, created by 2 | sphinx-quickstart. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to DemoApp's documentation! 7 | ==================================================================== 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | install 15 | deploy 16 | docker_ec2 17 | tests 18 | 19 | 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | * :ref:`modindex` 26 | * :ref:`search` 27 | -------------------------------------------------------------------------------- /demoapp/users/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import AbstractUser 2 | from django.db import models 3 | from django.urls import reverse 4 | from django.utils.translation import ugettext_lazy as _ 5 | 6 | 7 | class User(AbstractUser): 8 | 9 | # First Name and Last Name do not cover name patterns 10 | # around the globe. 11 | name = models.CharField(_("Name of User"), blank=True, max_length=255) 12 | 13 | def __str__(self): 14 | return self.username 15 | 16 | def get_absolute_url(self): 17 | return reverse("users:detail", kwargs={"username": self.username}) 18 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{py,rst,ini}] 12 | indent_style = space 13 | indent_size = 4 14 | 15 | [*.py] 16 | line_length=120 17 | known_first_party=demoapp 18 | multi_line_output=3 19 | default_section=THIRDPARTY 20 | 21 | [*.{html,css,scss,json,yml}] 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [*.md] 26 | trim_trailing_whitespace = false 27 | 28 | [Makefile] 29 | indent_style = tab 30 | 31 | [nginx.conf] 32 | indent_style = space 33 | indent_size = 2 34 | -------------------------------------------------------------------------------- /demoapp/templates/account/logout.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Sign Out" %}{% endblock %} 6 | 7 | {% block inner %} 8 |

{% trans "Sign Out" %}

9 | 10 |

{% trans 'Are you sure you want to sign out?' %}

11 | 12 |
13 | {% csrf_token %} 14 | {% if redirect_field_value %} 15 | 16 | {% endif %} 17 | 18 |
19 | 20 | 21 | {% endblock %} 22 | 23 | -------------------------------------------------------------------------------- /demoapp/contrib/sites/migrations/0002_alter_domain_unique.py: -------------------------------------------------------------------------------- 1 | import django.contrib.sites.models 2 | from django.db import migrations, models 3 | 4 | 5 | class Migration(migrations.Migration): 6 | 7 | dependencies = [("sites", "0001_initial")] 8 | 9 | operations = [ 10 | migrations.AlterField( 11 | model_name="site", 12 | name="domain", 13 | field=models.CharField( 14 | max_length=100, 15 | unique=True, 16 | validators=[django.contrib.sites.models._simple_domain_name_validator], 17 | verbose_name="domain name", 18 | ), 19 | ) 20 | ] 21 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/_sourced/messages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | message_newline() { 5 | echo 6 | } 7 | 8 | message_debug() 9 | { 10 | echo -e "DEBUG: ${@}" 11 | } 12 | 13 | message_welcome() 14 | { 15 | echo -e "\e[1m${@}\e[0m" 16 | } 17 | 18 | message_warning() 19 | { 20 | echo -e "\e[33mWARNING\e[0m: ${@}" 21 | } 22 | 23 | message_error() 24 | { 25 | echo -e "\e[31mERROR\e[0m: ${@}" 26 | } 27 | 28 | message_info() 29 | { 30 | echo -e "\e[37mINFO\e[0m: ${@}" 31 | } 32 | 33 | message_suggestion() 34 | { 35 | echo -e "\e[33mSUGGESTION\e[0m: ${@}" 36 | } 37 | 38 | message_success() 39 | { 40 | echo -e "\e[32mSUCCESS\e[0m: ${@}" 41 | } 42 | -------------------------------------------------------------------------------- /DummyData.md: -------------------------------------------------------------------------------- 1 | # Dummy data creation notes 2 | 3 | Superset visualizes the data available in django demo app. 4 | To simulate the flow, let's create some dummy data. 5 | 6 | Connect to postgres from host system 7 | `psql -h localhost -U debug -d demoapp` 8 | 9 | ##### Create table 10 | ```` 11 | CREATE TABLE student_enrollment_growth ( 12 | id SERIAL PRIMARY KEY, 13 | audited_on date, 14 | no_of_students integer 15 | ) 16 | ```` 17 | 18 | ```` 19 | INSERT INTO student_enrollment_growth (audited_on, no_of_students) 20 | values 21 | ('2014-01-01', 35), 22 | ('2014-06-01', 85), 23 | ('2015-01-01', 135), 24 | ('2015-06-01', 175), 25 | ('2016-01-01', 205), 26 | ('2016-06-01', 285), 27 | ('2017-01-01', 335), 28 | ('2017-06-01', 455), 29 | 30 | ```` -------------------------------------------------------------------------------- /demoapp/static/sass/project.scss: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | // project specific CSS goes here 6 | 7 | //////////////////////////////// 8 | //Variables// 9 | //////////////////////////////// 10 | 11 | // Alert colors 12 | 13 | $white: #fff; 14 | $mint-green: #d6e9c6; 15 | $black: #000; 16 | $pink: #f2dede; 17 | $dark-pink: #eed3d7; 18 | $red: #b94a48; 19 | 20 | //////////////////////////////// 21 | //Alerts// 22 | //////////////////////////////// 23 | 24 | // bootstrap alert CSS, translated to the django-standard levels of 25 | // debug, info, success, warning, error 26 | 27 | .alert-debug { 28 | background-color: $white; 29 | border-color: $mint-green; 30 | color: $black; 31 | } 32 | 33 | .alert-error { 34 | background-color: $pink; 35 | border-color: $dark-pink; 36 | color: $red; 37 | } 38 | -------------------------------------------------------------------------------- /demoapp/templates/account/signup.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | 6 | {% block head_title %}{% trans "Signup" %}{% endblock %} 7 | 8 | {% block inner %} 9 |

{% trans "Sign Up" %}

10 | 11 |

{% blocktrans %}Already have an account? Then please sign in.{% endblocktrans %}

12 | 13 | 21 | 22 | {% endblock %} 23 | 24 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DJANGO_MANAGE_LOCATION 7 | 8 | manage.py 9 | 10 | 11 | 12 | 13 | 14 | /${PROJECT_DIR_NAME} 15 | 16 | 17 | 18 | python interpreter 19 | 20 | Default 21 | 22 | 23 | -------------------------------------------------------------------------------- /demoapp/static/js/project.js: -------------------------------------------------------------------------------- 1 | /* Project specific Javascript goes here. */ 2 | 3 | /* 4 | Formatting hack to get around crispy-forms unfortunate hardcoding 5 | in helpers.FormHelper: 6 | 7 | if template_pack == 'bootstrap4': 8 | grid_colum_matcher = re.compile('\w*col-(xs|sm|md|lg|xl)-\d+\w*') 9 | using_grid_layout = (grid_colum_matcher.match(self.label_class) or 10 | grid_colum_matcher.match(self.field_class)) 11 | if using_grid_layout: 12 | items['using_grid_layout'] = True 13 | 14 | Issues with the above approach: 15 | 16 | 1. Fragile: Assumes Bootstrap 4's API doesn't change (it does) 17 | 2. Unforgiving: Doesn't allow for any variation in template design 18 | 3. Really Unforgiving: No way to override this behavior 19 | 4. Undocumented: No mention in the documentation, or it's too hard for me to find 20 | */ 21 | $('.form-group').removeClass('row'); 22 | -------------------------------------------------------------------------------- /demoapp/templates/account/verified_email_required.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | 5 | {% block head_title %}{% trans "Verify Your E-mail Address" %}{% endblock %} 6 | 7 | {% block inner %} 8 |

{% trans "Verify Your E-mail Address" %}

9 | 10 | {% url 'account_email' as email_url %} 11 | 12 |

{% blocktrans %}This part of the site requires us to verify that 13 | you are who you claim to be. For this purpose, we require that you 14 | verify ownership of your e-mail address. {% endblocktrans %}

15 | 16 |

{% blocktrans %}We have sent an e-mail to you for 17 | verification. Please click on the link inside this e-mail. Please 18 | contact us if you do not receive it within a few minutes.{% endblocktrans %}

19 | 20 |

{% blocktrans %}Note: you can still change your e-mail address.{% endblocktrans %}

21 | 22 | 23 | {% endblock %} 24 | 25 | -------------------------------------------------------------------------------- /demoapp/templates/users/user_detail.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load static %} 3 | 4 | {% block title %}User: {{ object.username }}{% endblock %} 5 | 6 | {% block content %} 7 |
8 | 9 |
10 |
11 | 12 |

{{ object.username }}

13 | {% if object.name %} 14 |

{{ object.name }}

15 | {% endif %} 16 |
17 |
18 | 19 | {% if object == request.user %} 20 | 21 |
22 | 23 |
24 | My Info 25 | E-Mail 26 | 27 |
28 | 29 |
30 | 31 | {% endif %} 32 | 33 | 34 |
35 | {% endblock content %} 36 | 37 | -------------------------------------------------------------------------------- /demoapp/templates/account/password_reset.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load account %} 5 | {% load crispy_forms_tags %} 6 | 7 | {% block head_title %}{% trans "Password Reset" %}{% endblock %} 8 | 9 | {% block inner %} 10 | 11 |

{% trans "Password Reset" %}

12 | {% if user.is_authenticated %} 13 | {% include "account/snippets/already_logged_in.html" %} 14 | {% endif %} 15 | 16 |

{% trans "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}

17 | 18 |
19 | {% csrf_token %} 20 | {{ form|crispy }} 21 | 22 |
23 | 24 |

{% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %}

25 | {% endblock %} 26 | 27 | -------------------------------------------------------------------------------- /nginx-conf/default.conf: -------------------------------------------------------------------------------- 1 | upstream superset-server { 2 | server superset:8088; 3 | } 4 | 5 | upstream django-server { 6 | server django:8000; 7 | } 8 | 9 | server { 10 | listen 8080; 11 | gzip_static on; 12 | gzip_disable "msie6"; 13 | gzip_proxied any; 14 | 15 | location ~ /.well-known { 16 | allow all; 17 | } 18 | 19 | location / { 20 | proxy_set_header X-Real-IP $remote_addr; 21 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 22 | proxy_set_header Host $http_host; 23 | proxy_pass http://django-server/; 24 | proxy_http_version 1.1; 25 | proxy_set_header Upgrade $http_upgrade; 26 | proxy_set_header Connection "Upgrade"; 27 | } 28 | 29 | location /superset { 30 | proxy_set_header X-Real-IP $remote_addr; 31 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 32 | proxy_set_header Host $http_host; 33 | proxy_pass http://superset:8088/; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /compose/local/django/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | 3 | ENV PYTHONUNBUFFERED 1 4 | 5 | RUN apk update \ 6 | # psycopg2 dependencies 7 | && apk add --virtual build-deps gcc python3-dev musl-dev \ 8 | && apk add postgresql-dev \ 9 | # Pillow dependencies 10 | && apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \ 11 | # CFFI dependencies 12 | && apk add libffi-dev py-cffi \ 13 | # Translations dependencies 14 | && apk add gettext \ 15 | # https://docs.djangoproject.com/en/dev/ref/django-admin/#dbshell 16 | && apk add postgresql-client \ 17 | && apk add --no-cache git 18 | 19 | # Requirements are installed here to ensure they will be cached. 20 | COPY ./requirements /requirements 21 | RUN pip install -r /requirements/local.txt 22 | 23 | COPY ./compose/production/django/entrypoint /entrypoint 24 | RUN sed -i 's/\r//' /entrypoint 25 | RUN chmod +x /entrypoint 26 | 27 | COPY ./compose/local/django/start /start 28 | RUN sed -i 's/\r//' /start 29 | RUN chmod +x /start 30 | 31 | WORKDIR /app 32 | 33 | ENTRYPOINT ["/entrypoint"] 34 | -------------------------------------------------------------------------------- /demoapp/templates/account/password_reset_from_key.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load crispy_forms_tags %} 5 | {% block head_title %}{% trans "Change Password" %}{% endblock %} 6 | 7 | {% block inner %} 8 |

{% if token_fail %}{% trans "Bad Token" %}{% else %}{% trans "Change Password" %}{% endif %}

9 | 10 | {% if token_fail %} 11 | {% url 'account_reset_password' as passwd_reset_url %} 12 |

{% blocktrans %}The password reset link was invalid, possibly because it has already been used. Please request a new password reset.{% endblocktrans %}

13 | {% else %} 14 | {% if form %} 15 |
16 | {% csrf_token %} 17 | {{ form|crispy }} 18 | 19 |
20 | {% else %} 21 |

{% trans 'Your password is now changed.' %}

22 | {% endif %} 23 | {% endif %} 24 | {% endblock %} 25 | 26 | -------------------------------------------------------------------------------- /demoapp/templates/account/email_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load account %} 5 | 6 | {% block head_title %}{% trans "Confirm E-mail Address" %}{% endblock %} 7 | 8 | 9 | {% block inner %} 10 |

{% trans "Confirm E-mail Address" %}

11 | 12 | {% if confirmation %} 13 | 14 | {% user_display confirmation.email_address.user as user_display %} 15 | 16 |

{% blocktrans with confirmation.email_address.email as email %}Please confirm that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktrans %}

17 | 18 |
19 | {% csrf_token %} 20 | 21 |
22 | 23 | {% else %} 24 | 25 | {% url 'account_email' as email_url %} 26 | 27 |

{% blocktrans %}This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request.{% endblocktrans %}

28 | 29 | {% endif %} 30 | 31 | {% endblock %} 32 | 33 | -------------------------------------------------------------------------------- /compose/production/django/entrypoint: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit 4 | set -o pipefail 5 | set -o nounset 6 | 7 | 8 | # N.B. If only .env files supported variable expansion... 9 | export CELERY_BROKER_URL="${REDIS_URL}" 10 | 11 | if [ -z "${POSTGRES_USER}" ]; then 12 | base_postgres_image_default_user='postgres' 13 | export POSTGRES_USER="${base_postgres_image_default_user}" 14 | fi 15 | export DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}" 16 | 17 | postgres_ready() { 18 | python << END 19 | import sys 20 | 21 | import psycopg2 22 | 23 | try: 24 | psycopg2.connect( 25 | dbname="${POSTGRES_DB}", 26 | user="${POSTGRES_USER}", 27 | password="${POSTGRES_PASSWORD}", 28 | host="${POSTGRES_HOST}", 29 | port="${POSTGRES_PORT}", 30 | ) 31 | except psycopg2.OperationalError: 32 | sys.exit(-1) 33 | sys.exit(0) 34 | 35 | END 36 | } 37 | until postgres_ready; do 38 | >&2 echo 'Waiting for PostgreSQL to become available...' 39 | sleep 1 40 | done 41 | >&2 echo 'PostgreSQL is available' 42 | 43 | exec "$@" 44 | -------------------------------------------------------------------------------- /requirements/base.txt: -------------------------------------------------------------------------------- 1 | pytz==2018.4 # https://github.com/stub42/pytz 2 | python-slugify==1.2.5 # https://github.com/un33k/python-slugify 3 | Pillow==5.1.0 # https://github.com/python-pillow/Pillow 4 | argon2-cffi==18.1.0 # https://github.com/hynek/argon2_cffi 5 | redis>=2.10.5 # https://github.com/antirez/redis 6 | 7 | # Django 8 | # ------------------------------------------------------------------------------ 9 | django==2.0.6 # pyup: < 2.1 # https://www.djangoproject.com/ 10 | django-environ==0.4.4 # https://github.com/joke2k/django-environ 11 | django-model-utils==3.1.2 # https://github.com/jazzband/django-model-utils 12 | django-allauth==0.36.0 # https://github.com/pennersr/django-allauth 13 | django-crispy-forms==1.7.2 # https://github.com/django-crispy-forms/django-crispy-forms 14 | django-redis==4.9.0 # https://github.com/niwinz/django-redis 15 | 16 | # Django REST Framework 17 | djangorestframework==3.8.2 # https://github.com/encode/django-rest-framework 18 | coreapi==2.3.3 # https://github.com/core-api/python-client 19 | 20 | # KeyCloak integration 21 | git+https://github.com/Peter-Slump/django-keycloak.git@7e325e8 -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local") 7 | 8 | try: 9 | from django.core.management import execute_from_command_line 10 | except ImportError: 11 | # The above import may fail for some other reason. Ensure that the 12 | # issue is really that Django is missing to avoid masking other 13 | # exceptions on Python 2. 14 | try: 15 | import django # noqa 16 | except ImportError: 17 | raise ImportError( 18 | "Couldn't import Django. Are you sure it's installed and " 19 | "available on your PYTHONPATH environment variable? Did you " 20 | "forget to activate a virtual environment?" 21 | ) 22 | 23 | raise 24 | 25 | # This allows easy placement of apps within the interior 26 | # demoapp directory. 27 | current_path = os.path.dirname(os.path.abspath(__file__)) 28 | sys.path.append(os.path.join(current_path, "demoapp")) 29 | 30 | execute_from_command_line(sys.argv) 31 | -------------------------------------------------------------------------------- /compose/production/django/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | 3 | ENV PYTHONUNBUFFERED 1 4 | 5 | RUN apk update \ 6 | # psycopg2 dependencies 7 | && apk add --virtual build-deps gcc python3-dev musl-dev \ 8 | && apk add postgresql-dev \ 9 | # Pillow dependencies 10 | && apk add jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev \ 11 | # CFFI dependencies 12 | && apk add libffi-dev py-cffi 13 | 14 | RUN addgroup -S django \ 15 | && adduser -S -G django django 16 | 17 | # Requirements are installed here to ensure they will be cached. 18 | COPY ./requirements /requirements 19 | RUN pip install --no-cache-dir -r /requirements/production.txt \ 20 | && rm -rf /requirements 21 | 22 | COPY ./compose/production/django/entrypoint /entrypoint 23 | RUN sed -i 's/\r//' /entrypoint 24 | RUN chmod +x /entrypoint 25 | RUN chown django /entrypoint 26 | 27 | COPY ./compose/production/django/start /start 28 | RUN sed -i 's/\r//' /start 29 | RUN chmod +x /start 30 | RUN chown django /start 31 | 32 | COPY . /app 33 | 34 | RUN chown -R django /app 35 | 36 | USER django 37 | 38 | WORKDIR /app 39 | 40 | ENTRYPOINT ["/entrypoint"] 41 | -------------------------------------------------------------------------------- /demoapp/contrib/sites/migrations/0003_set_site_domain_and_name.py: -------------------------------------------------------------------------------- 1 | """ 2 | To understand why this file is here, please read: 3 | 4 | http://cookiecutter-django.readthedocs.io/en/latest/faq.html#why-is-there-a-django-contrib-sites-directory-in-cookiecutter-django 5 | """ 6 | from django.conf import settings 7 | from django.db import migrations 8 | 9 | 10 | def update_site_forward(apps, schema_editor): 11 | """Set site domain and name.""" 12 | Site = apps.get_model("sites", "Site") 13 | Site.objects.update_or_create( 14 | id=settings.SITE_ID, 15 | defaults={ 16 | "domain": "example.com", 17 | "name": "DemoApp", 18 | }, 19 | ) 20 | 21 | 22 | def update_site_backward(apps, schema_editor): 23 | """Revert site domain and name to default.""" 24 | Site = apps.get_model("sites", "Site") 25 | Site.objects.update_or_create( 26 | id=settings.SITE_ID, defaults={"domain": "example.com", "name": "example.com"} 27 | ) 28 | 29 | 30 | class Migration(migrations.Migration): 31 | 32 | dependencies = [("sites", "0002_alter_domain_unique")] 33 | 34 | operations = [migrations.RunPython(update_site_forward, update_site_backward)] 35 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/backup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | ### Create a database backup. 5 | ### 6 | ### Usage: 7 | ### $ docker-compose -f .yml (exec |run --rm) postgres backup 8 | 9 | 10 | set -o errexit 11 | set -o pipefail 12 | set -o nounset 13 | 14 | 15 | working_dir="$(dirname ${0})" 16 | source "${working_dir}/_sourced/constants.sh" 17 | source "${working_dir}/_sourced/messages.sh" 18 | 19 | 20 | message_welcome "Backing up the '${POSTGRES_DB}' database..." 21 | 22 | 23 | if [[ "${POSTGRES_USER}" == "postgres" ]]; then 24 | message_error "Backing up as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again." 25 | exit 1 26 | fi 27 | 28 | export PGHOST="${POSTGRES_HOST}" 29 | export PGPORT="${POSTGRES_PORT}" 30 | export PGUSER="${POSTGRES_USER}" 31 | export PGPASSWORD="${POSTGRES_PASSWORD}" 32 | export PGDATABASE="${POSTGRES_DB}" 33 | 34 | backup_filename="${BACKUP_FILE_PREFIX}_$(date +'%Y_%m_%dT%H_%M_%S').sql.gz" 35 | pg_dump | gzip > "${BACKUP_DIR_PATH}/${backup_filename}" 36 | 37 | 38 | message_success "'${POSTGRES_DB}' database backup '${backup_filename}' has been created and placed in '${BACKUP_DIR_PATH}'." 39 | -------------------------------------------------------------------------------- /production.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | 3 | volumes: 4 | postgres_data: {} 5 | postgres_backup: {} 6 | caddy: {} 7 | 8 | services: 9 | django: 10 | build: 11 | context: . 12 | dockerfile: ./compose/production/django/Dockerfile 13 | image: demoapp_production_django 14 | depends_on: 15 | - postgres 16 | - redis 17 | env_file: 18 | - ./.envs/.production/.django 19 | - ./.envs/.production/.postgres 20 | command: /start 21 | 22 | postgres: 23 | build: 24 | context: . 25 | dockerfile: ./compose/production/postgres/Dockerfile 26 | image: demoapp_production_postgres 27 | volumes: 28 | - postgres_data:/var/lib/postgresql/data 29 | - postgres_backup:/backups 30 | env_file: 31 | - ./.envs/.production/.postgres 32 | 33 | caddy: 34 | build: 35 | context: . 36 | dockerfile: ./compose/production/caddy/Dockerfile 37 | image: demoapp_production_caddy 38 | depends_on: 39 | - django 40 | volumes: 41 | - caddy:/root/.caddy 42 | env_file: 43 | - ./.envs/.production/.caddy 44 | ports: 45 | - "0.0.0.0:80:80" 46 | - "0.0.0.0:443:443" 47 | 48 | redis: 49 | image: redis:3.2 50 | -------------------------------------------------------------------------------- /demoapp/users/admin.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib import admin 3 | from django.contrib.auth.admin import UserAdmin as AuthUserAdmin 4 | from django.contrib.auth.forms import UserChangeForm, UserCreationForm 5 | from .models import User 6 | 7 | 8 | class MyUserChangeForm(UserChangeForm): 9 | 10 | class Meta(UserChangeForm.Meta): 11 | model = User 12 | 13 | 14 | class MyUserCreationForm(UserCreationForm): 15 | 16 | error_message = UserCreationForm.error_messages.update( 17 | {"duplicate_username": "This username has already been taken."} 18 | ) 19 | 20 | class Meta(UserCreationForm.Meta): 21 | model = User 22 | 23 | def clean_username(self): 24 | username = self.cleaned_data["username"] 25 | try: 26 | User.objects.get(username=username) 27 | except User.DoesNotExist: 28 | return username 29 | 30 | raise forms.ValidationError(self.error_messages["duplicate_username"]) 31 | 32 | 33 | @admin.register(User) 34 | class MyUserAdmin(AuthUserAdmin): 35 | form = MyUserChangeForm 36 | add_form = MyUserCreationForm 37 | fieldsets = (("User Profile", {"fields": ("name",)}),) + AuthUserAdmin.fieldsets 38 | list_display = ("username", "name", "is_superuser") 39 | search_fields = ["name"] 40 | -------------------------------------------------------------------------------- /requirements/local.txt: -------------------------------------------------------------------------------- 1 | -r ./base.txt 2 | 3 | Werkzeug==0.14.1 # https://github.com/pallets/werkzeug 4 | ipdb==0.11 # https://github.com/gotcha/ipdb 5 | Sphinx==1.7.5 # https://github.com/sphinx-doc/sphinx 6 | psycopg2==2.7.4 --no-binary psycopg2 # https://github.com/psycopg/psycopg2 7 | 8 | # Testing 9 | # ------------------------------------------------------------------------------ 10 | pytest==3.6.0 # https://github.com/pytest-dev/pytest 11 | pytest-sugar==0.9.1 # https://github.com/Frozenball/pytest-sugar 12 | 13 | # Code quality 14 | # ------------------------------------------------------------------------------ 15 | flake8==3.5.0 # https://github.com/PyCQA/flake8 16 | coverage==4.5.1 # https://github.com/nedbat/coveragepy 17 | 18 | # Django 19 | # ------------------------------------------------------------------------------ 20 | factory-boy==2.11.1 # https://github.com/FactoryBoy/factory_boy 21 | django-test-plus==1.0.22 # https://github.com/revsys/django-test-plus 22 | 23 | django-debug-toolbar==1.9.1 # https://github.com/jazzband/django-debug-toolbar 24 | django-extensions==2.0.7 # https://github.com/django-extensions/django-extensions 25 | django-coverage-plugin==1.5.0 # https://github.com/nedbat/django_coverage_plugin 26 | pytest-django==3.2.1 # https://github.com/pytest-dev/pytest-django 27 | -------------------------------------------------------------------------------- /superset-conf/config/security.py: -------------------------------------------------------------------------------- 1 | from flask import redirect, g, flash, request 2 | from flask_appbuilder.security.views import UserDBModelView,AuthDBView 3 | from superset.security import SupersetSecurityManager 4 | from flask_appbuilder.security.views import expose 5 | from flask_appbuilder.security.manager import BaseSecurityManager 6 | from flask_login import login_user, logout_user 7 | 8 | 9 | class CustomAuthDBView(AuthDBView): 10 | login_template = 'appbuilder/general/security/login_db.html' 11 | 12 | @expose('/login/', methods=['GET', 'POST']) 13 | def login(self): 14 | if request.args.get('username') is not None: 15 | user = self.appbuilder.sm.find_user(username=request.args.get('username')) 16 | flash('Admin auto logged in', 'success') 17 | login_user(user, remember=False) 18 | return redirect(self.appbuilder.get_url_for_index) 19 | elif g.user is not None and g.user.is_authenticated(): 20 | return redirect(self.appbuilder.get_url_for_index) 21 | else: 22 | flash('Unable to auto login', 'warning') 23 | return super(CustomAuthDBView,self).login() 24 | 25 | class CustomSecurityManager(SupersetSecurityManager): 26 | authdbview = CustomAuthDBView 27 | def __init__(self, appbuilder): 28 | super(CustomSecurityManager, self).__init__(appbuilder) 29 | 30 | -------------------------------------------------------------------------------- /superset-conf/config/superset_config.py: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------- 2 | # Superset specific config 3 | #--------------------------------------------------------- 4 | ROW_LIMIT = 5000 5 | 6 | SUPERSET_WEBSERVER_PORT = 8088 7 | #--------------------------------------------------------- 8 | 9 | #--------------------------------------------------------- 10 | # Flask App Builder configuration 11 | #--------------------------------------------------------- 12 | # Your App secret key 13 | SECRET_KEY = '\2\1thisismyscretkey\1\2\e\y\y\h' 14 | 15 | # The SQLAlchemy connection string to your database backend 16 | # This connection defines the path to the database that stores your 17 | # superset metadata (slices, connections, tables, dashboards, ...). 18 | # Note that the connection information to connect to the datasources 19 | # you want to explore are managed directly in the web UI 20 | SQLALCHEMY_DATABASE_URI = 'sqlite:////var/lib/superset/superset.db' 21 | 22 | # Flask-WTF flag for CSRF 23 | WTF_CSRF_ENABLED = False 24 | # Add endpoints that need to be exempt from CSRF protection 25 | WTF_CSRF_EXEMPT_LIST = [] 26 | # A CSRF token that expires in 1 year 27 | WTF_CSRF_TIME_LIMIT = 60 * 60 * 24 * 365 28 | 29 | # Set this API key to enable Mapbox visualizations 30 | MAPBOX_API_KEY = '' 31 | ENABLE_PROXY_FIX = True 32 | 33 | from security import CustomSecurityManager 34 | CUSTOM_SECURITY_MANAGER = CustomSecurityManager -------------------------------------------------------------------------------- /demoapp/users/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.mixins import LoginRequiredMixin 2 | from django.urls import reverse 3 | from django.views.generic import DetailView, ListView, RedirectView, UpdateView 4 | 5 | from .models import User 6 | 7 | 8 | class UserDetailView(LoginRequiredMixin, DetailView): 9 | model = User 10 | # These next two lines tell the view to index lookups by username 11 | slug_field = "username" 12 | slug_url_kwarg = "username" 13 | 14 | 15 | class UserRedirectView(LoginRequiredMixin, RedirectView): 16 | permanent = False 17 | 18 | def get_redirect_url(self): 19 | return reverse("users:detail", kwargs={"username": self.request.user.username}) 20 | 21 | 22 | class UserUpdateView(LoginRequiredMixin, UpdateView): 23 | 24 | fields = ["name"] 25 | 26 | # we already imported User in the view code above, remember? 27 | model = User 28 | 29 | # send the user back to their own page after a successful update 30 | 31 | def get_success_url(self): 32 | return reverse("users:detail", kwargs={"username": self.request.user.username}) 33 | 34 | def get_object(self): 35 | # Only get the User record for the user making the request 36 | return User.objects.get(username=self.request.user.username) 37 | 38 | 39 | class UserListView(LoginRequiredMixin, ListView): 40 | model = User 41 | # These next two lines tell the view to index lookups by username 42 | slug_field = "username" 43 | slug_url_kwarg = "username" 44 | -------------------------------------------------------------------------------- /demoapp/contrib/sites/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | import django.contrib.sites.models 2 | from django.contrib.sites.models import _simple_domain_name_validator 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [] 9 | 10 | operations = [ 11 | migrations.CreateModel( 12 | name="Site", 13 | fields=[ 14 | ( 15 | "id", 16 | models.AutoField( 17 | verbose_name="ID", 18 | serialize=False, 19 | auto_created=True, 20 | primary_key=True, 21 | ), 22 | ), 23 | ( 24 | "domain", 25 | models.CharField( 26 | max_length=100, 27 | verbose_name="domain name", 28 | validators=[_simple_domain_name_validator], 29 | ), 30 | ), 31 | ("name", models.CharField(max_length=50, verbose_name="display name")), 32 | ], 33 | options={ 34 | "ordering": ("domain",), 35 | "db_table": "django_site", 36 | "verbose_name": "site", 37 | "verbose_name_plural": "sites", 38 | }, 39 | bases=(models.Model,), 40 | managers=[("objects", django.contrib.sites.models.SiteManager())], 41 | ) 42 | ] 43 | -------------------------------------------------------------------------------- /demoapp/users/tests/test_admin.py: -------------------------------------------------------------------------------- 1 | from test_plus.test import TestCase 2 | 3 | from ..admin import MyUserCreationForm 4 | 5 | 6 | class TestMyUserCreationForm(TestCase): 7 | 8 | def setUp(self): 9 | self.user = self.make_user("notalamode", "notalamodespassword") 10 | 11 | def test_clean_username_success(self): 12 | # Instantiate the form with a new username 13 | form = MyUserCreationForm( 14 | { 15 | "username": "alamode", 16 | "password1": "7jefB#f@Cc7YJB]2v", 17 | "password2": "7jefB#f@Cc7YJB]2v", 18 | } 19 | ) 20 | # Run is_valid() to trigger the validation 21 | valid = form.is_valid() 22 | self.assertTrue(valid) 23 | 24 | # Run the actual clean_username method 25 | username = form.clean_username() 26 | self.assertEqual("alamode", username) 27 | 28 | def test_clean_username_false(self): 29 | # Instantiate the form with the same username as self.user 30 | form = MyUserCreationForm( 31 | { 32 | "username": self.user.username, 33 | "password1": "notalamodespassword", 34 | "password2": "notalamodespassword", 35 | } 36 | ) 37 | # Run is_valid() to trigger the validation, which is going to fail 38 | # because the username is already taken 39 | valid = form.is_valid() 40 | self.assertFalse(valid) 41 | 42 | # The form.errors dict should contain a single error called 'username' 43 | self.assertTrue(len(form.errors) == 1) 44 | self.assertTrue("username" in form.errors) 45 | -------------------------------------------------------------------------------- /demoapp/templates/account/login.html: -------------------------------------------------------------------------------- 1 | {% extends "account/base.html" %} 2 | 3 | {% load i18n %} 4 | {% load account socialaccount %} 5 | {% load crispy_forms_tags %} 6 | 7 | {% block head_title %}{% trans "Sign In" %}{% endblock %} 8 | 9 | {% block inner %} 10 | 11 |

{% trans "Sign In" %}

12 | 13 | {% get_providers as socialaccount_providers %} 14 | 15 | {% if socialaccount_providers %} 16 |

{% blocktrans with site.name as site_name %}Please sign in with one 17 | of your existing third party accounts. Or, sign up 18 | for a {{ site_name }} account and sign in below:{% endblocktrans %}

19 | 20 |
21 | 22 |
    23 | {% include "socialaccount/snippets/provider_list.html" with process="login" %} 24 |
25 | 26 | 27 | 28 |
29 | 30 | {% include "socialaccount/snippets/login_extra.html" %} 31 | 32 | {% else %} 33 |

{% blocktrans %}If you have not created an account yet, then please 34 | sign up first.{% endblocktrans %}

35 | {% endif %} 36 | 37 | 46 | 47 | {% endblock %} 48 | 49 | -------------------------------------------------------------------------------- /local.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | 3 | volumes: 4 | postgres_data_local: {} 5 | postgres_backup_local: {} 6 | 7 | services: 8 | django: 9 | build: 10 | context: . 11 | dockerfile: ./compose/local/django/Dockerfile 12 | image: demoapp_local_django 13 | depends_on: 14 | - postgres 15 | - mailhog 16 | volumes: 17 | - .:/app 18 | env_file: 19 | - ./.envs/.local/.django 20 | - ./.envs/.local/.postgres 21 | ports: 22 | - "8000:8000" 23 | command: /start 24 | networks: 25 | - demo-app 26 | 27 | 28 | postgres: 29 | build: 30 | context: . 31 | dockerfile: ./compose/production/postgres/Dockerfile 32 | image: demoapp_production_postgres 33 | volumes: 34 | - postgres_data_local:/var/lib/postgresql/data 35 | - postgres_backup_local:/backups 36 | env_file: 37 | - ./.envs/.local/.postgres 38 | ports: 39 | - "5432:5432" 40 | networks: 41 | - demo-app 42 | 43 | mailhog: 44 | image: mailhog/mailhog:v1.0.0 45 | ports: 46 | - "8025:8025" 47 | environment: 48 | - MH_UI_WEB_PATH=mailhog 49 | networks: 50 | - demo-app 51 | 52 | superset: 53 | image: amancevice/superset:0.25.6 54 | ports: 55 | - "8088:8088" 56 | volumes: 57 | - ./superset-conf/config:/etc/superset 58 | - ./superset-conf/data:/var/lib/superset 59 | networks: 60 | - demo-app 61 | 62 | nginx: 63 | image: nginx:1.15 64 | volumes: 65 | - ./nginx-conf/nginx.conf:/etc/nginx/nginx.conf:ro 66 | ports: 67 | - "8080:8080" 68 | - "8081:8081" 69 | networks: 70 | - demo-app 71 | depends_on: 72 | - django 73 | - superset 74 | 75 | networks: 76 | demo-app: 77 | -------------------------------------------------------------------------------- /demoapp/users/tests/test_urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import reverse, resolve 2 | 3 | from test_plus.test import TestCase 4 | 5 | 6 | class TestUserURLs(TestCase): 7 | """Test URL patterns for users app.""" 8 | 9 | def setUp(self): 10 | self.user = self.make_user() 11 | 12 | def test_list_reverse(self): 13 | """users:list should reverse to /users/.""" 14 | self.assertEqual(reverse("users:list"), "/users/") 15 | 16 | def test_list_resolve(self): 17 | """/users/ should resolve to users:list.""" 18 | self.assertEqual(resolve("/users/").view_name, "users:list") 19 | 20 | def test_redirect_reverse(self): 21 | """users:redirect should reverse to /users/~redirect/.""" 22 | self.assertEqual(reverse("users:redirect"), "/users/~redirect/") 23 | 24 | def test_redirect_resolve(self): 25 | """/users/~redirect/ should resolve to users:redirect.""" 26 | self.assertEqual(resolve("/users/~redirect/").view_name, "users:redirect") 27 | 28 | def test_detail_reverse(self): 29 | """users:detail should reverse to /users/testuser/.""" 30 | self.assertEqual( 31 | reverse("users:detail", kwargs={"username": "testuser"}), "/users/testuser/" 32 | ) 33 | 34 | def test_detail_resolve(self): 35 | """/users/testuser/ should resolve to users:detail.""" 36 | self.assertEqual(resolve("/users/testuser/").view_name, "users:detail") 37 | 38 | def test_update_reverse(self): 39 | """users:update should reverse to /users/~update/.""" 40 | self.assertEqual(reverse("users:update"), "/users/~update/") 41 | 42 | def test_update_resolve(self): 43 | """/users/~update/ should resolve to users:update.""" 44 | self.assertEqual(resolve("/users/~update/").view_name, "users:update") 45 | -------------------------------------------------------------------------------- /config/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for DemoApp project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | import sys 18 | 19 | from django.core.wsgi import get_wsgi_application 20 | 21 | # This allows easy placement of apps within the interior 22 | # demoapp directory. 23 | app_path = os.path.abspath(os.path.join( 24 | os.path.dirname(os.path.abspath(__file__)), os.pardir)) 25 | sys.path.append(os.path.join(app_path, 'demoapp')) 26 | 27 | # We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks 28 | # if running multiple sites in the same mod_wsgi process. To fix this, use 29 | # mod_wsgi daemon mode with each site in its own daemon process, or use 30 | # os.environ["DJANGO_SETTINGS_MODULE"] = "config.settings.production" 31 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.production") 32 | 33 | # This application object is used by any WSGI server configured to use this 34 | # file. This includes Django's development server, if the WSGI_APPLICATION 35 | # setting points here. 36 | application = get_wsgi_application() 37 | 38 | # Apply WSGI middleware here. 39 | # from helloworld.wsgi import HelloWorldApplication 40 | # application = HelloWorldApplication(application) 41 | -------------------------------------------------------------------------------- /utility/install_python_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WORK_DIR="$(dirname "$0")" 4 | PROJECT_DIR="$(dirname "$WORK_DIR")" 5 | 6 | pip --version >/dev/null 2>&1 || { 7 | echo >&2 -e "\npip is required but it's not installed." 8 | echo >&2 -e "You can install it by running the following command:\n" 9 | echo >&2 "wget https://bootstrap.pypa.io/get-pip.py --output-document=get-pip.py; chmod +x get-pip.py; sudo -H python3 get-pip.py" 10 | echo >&2 -e "\n" 11 | echo >&2 -e "\nFor more information, see pip documentation: https://pip.pypa.io/en/latest/" 12 | exit 1; 13 | } 14 | 15 | virtualenv --version >/dev/null 2>&1 || { 16 | echo >&2 -e "\nvirtualenv is required but it's not installed." 17 | echo >&2 -e "You can install it by running the following command:\n" 18 | echo >&2 "sudo -H pip3 install virtualenv" 19 | echo >&2 -e "\n" 20 | echo >&2 -e "\nFor more information, see virtualenv documentation: https://virtualenv.pypa.io/en/latest/" 21 | exit 1; 22 | } 23 | 24 | if [ -z "$VIRTUAL_ENV" ]; then 25 | echo >&2 -e "\nYou need activate a virtualenv first" 26 | echo >&2 -e 'If you do not have a virtualenv created, run the following command to create and automatically activate a new virtualenv named "venv" on current folder:\n' 27 | echo >&2 -e "virtualenv venv --python=\`which python3\`" 28 | echo >&2 -e "\nTo leave/disable the currently active virtualenv, run the following command:\n" 29 | echo >&2 "deactivate" 30 | echo >&2 -e "\nTo activate the virtualenv again, run the following command:\n" 31 | echo >&2 "source venv/bin/activate" 32 | echo >&2 -e "\nFor more information, see virtualenv documentation: https://virtualenv.pypa.io/en/latest/" 33 | echo >&2 -e "\n" 34 | exit 1; 35 | else 36 | 37 | pip install -r $PROJECT_DIR/requirements/local.txt 38 | 39 | fi 40 | -------------------------------------------------------------------------------- /compose/production/postgres/maintenance/restore: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | 4 | ### Restore database from a backup. 5 | ### 6 | ### Parameters: 7 | ### <1> filename of an existing backup. 8 | ### 9 | ### Usage: 10 | ### $ docker-compose -f .yml (exec |run --rm) postgres restore <1> 11 | 12 | 13 | set -o errexit 14 | set -o pipefail 15 | set -o nounset 16 | 17 | 18 | working_dir="$(dirname ${0})" 19 | source "${working_dir}/_sourced/constants.sh" 20 | source "${working_dir}/_sourced/messages.sh" 21 | 22 | 23 | if [[ -z ${1+x} ]]; then 24 | message_error "Backup filename is not specified yet it is a required parameter. Make sure you provide one and try again." 25 | exit 1 26 | fi 27 | backup_filename="${BACKUP_DIR_PATH}/${1}" 28 | if [[ ! -f "${backup_filename}" ]]; then 29 | message_error "No backup with the specified filename found. Check out the 'backups' maintenance script output to see if there is one and try again." 30 | exit 1 31 | fi 32 | 33 | message_welcome "Restoring the '${POSTGRES_DB}' database from the '${backup_filename}' backup..." 34 | 35 | if [[ "${POSTGRES_USER}" == "postgres" ]]; then 36 | message_error "Restoring as 'postgres' user is not supported. Assign 'POSTGRES_USER' env with another one and try again." 37 | exit 1 38 | fi 39 | 40 | export PGHOST="${POSTGRES_HOST}" 41 | export PGPORT="${POSTGRES_PORT}" 42 | export PGUSER="${POSTGRES_USER}" 43 | export PGPASSWORD="${POSTGRES_PASSWORD}" 44 | export PGDATABASE="${POSTGRES_DB}" 45 | 46 | message_info "Dropping the database..." 47 | dropdb "${PGDATABASE}" 48 | 49 | message_info "Creating a new database..." 50 | createdb --owner="${POSTGRES_USER}" 51 | 52 | message_info "Applying the backup to the new database..." 53 | gunzip -c "${backup_filename}" | psql "${POSTGRES_DB}" 54 | 55 | message_success "The '${POSTGRES_DB}' database has been restored from the '${backup_filename}' backup." 56 | -------------------------------------------------------------------------------- /demoapp/users/tests/test_views.py: -------------------------------------------------------------------------------- 1 | from django.test import RequestFactory 2 | 3 | from test_plus.test import TestCase 4 | 5 | from ..views import UserRedirectView, UserUpdateView 6 | 7 | 8 | class BaseUserTestCase(TestCase): 9 | 10 | def setUp(self): 11 | self.user = self.make_user() 12 | self.factory = RequestFactory() 13 | 14 | 15 | class TestUserRedirectView(BaseUserTestCase): 16 | 17 | def test_get_redirect_url(self): 18 | # Instantiate the view directly. Never do this outside a test! 19 | view = UserRedirectView() 20 | # Generate a fake request 21 | request = self.factory.get("/fake-url") 22 | # Attach the user to the request 23 | request.user = self.user 24 | # Attach the request to the view 25 | view.request = request 26 | # Expect: '/users/testuser/', as that is the default username for 27 | # self.make_user() 28 | self.assertEqual(view.get_redirect_url(), "/users/testuser/") 29 | 30 | 31 | class TestUserUpdateView(BaseUserTestCase): 32 | 33 | def setUp(self): 34 | # call BaseUserTestCase.setUp() 35 | super(TestUserUpdateView, self).setUp() 36 | # Instantiate the view directly. Never do this outside a test! 37 | self.view = UserUpdateView() 38 | # Generate a fake request 39 | request = self.factory.get("/fake-url") 40 | # Attach the user to the request 41 | request.user = self.user 42 | # Attach the request to the view 43 | self.view.request = request 44 | 45 | def test_get_success_url(self): 46 | # Expect: '/users/testuser/', as that is the default username for 47 | # self.make_user() 48 | self.assertEqual(self.view.get_success_url(), "/users/testuser/") 49 | 50 | def test_get_object(self): 51 | # Expect: self.user, as that is the request's user object 52 | self.assertEqual(self.view.get_object(), self.user) 53 | -------------------------------------------------------------------------------- /nginx-conf/nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | worker_processes 1; 3 | 4 | 5 | events { 6 | worker_connections 1024; 7 | } 8 | 9 | 10 | http { 11 | include mime.types; 12 | default_type application/octet-stream; 13 | 14 | sendfile on; 15 | keepalive_timeout 65; 16 | proxy_set_header X-Real-IP $remote_addr; 17 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 18 | proxy_set_header Host $host:$server_port; 19 | proxy_set_header X-Forwarded-Host $server_name; 20 | chunked_transfer_encoding on; 21 | proxy_set_header X-NginX-Proxy true; 22 | proxy_set_header Upgrade $http_upgrade; 23 | proxy_set_header Connection "upgrade"; 24 | proxy_http_version 1.1; 25 | proxy_redirect off; 26 | proxy_buffering off; 27 | 28 | upstream superset-server { 29 | server superset:8088; 30 | } 31 | 32 | upstream django-server { 33 | server django:8000; 34 | } 35 | 36 | upstream mailhog { 37 | server mailhog:8025; 38 | } 39 | 40 | server { 41 | listen 8080; 42 | gzip_static on; 43 | gzip_disable "msie6"; 44 | gzip_proxied any; 45 | 46 | location / { 47 | proxy_pass http://django-server/; 48 | proxy_redirect off; 49 | } 50 | 51 | location /ss { 52 | proxy_pass http://nginx:8081/; 53 | } 54 | 55 | location /mailhog { 56 | proxy_pass http://mailhog/mailhog; 57 | } 58 | } 59 | 60 | server { 61 | # Once we have a domain name, both django and superset will run on same 80 port 62 | # they will have different server name like viz.domain.com and www.domain.com 63 | listen 8081; 64 | gzip_static on; 65 | gzip_disable "msie6"; 66 | gzip_proxied any; 67 | 68 | location / { 69 | proxy_pass http://superset:8088/; 70 | } 71 | 72 | } 73 | } 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /config/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | from django.urls import include, path 3 | from django.conf.urls.static import static 4 | from django.contrib import admin 5 | from django.views.generic import TemplateView 6 | from django.views import defaults as default_views 7 | from django_keycloak import views as keycloak_views 8 | 9 | urlpatterns = [ 10 | path("", TemplateView.as_view(template_name="pages/home.html"), name="home"), 11 | path( 12 | "about/", 13 | TemplateView.as_view(template_name="pages/about.html"), 14 | name="about", 15 | ), 16 | # Django Admin, use {% url 'admin:index' %} 17 | path(settings.ADMIN_URL, admin.site.urls), 18 | # User management 19 | path( 20 | "users/", 21 | include("demoapp.users.urls", namespace="users"), 22 | ), 23 | path("accounts/", include("allauth.urls")), 24 | path("login", keycloak_views.Login.as_view(), name="keycloak_login"), 25 | path("login-complete",keycloak_views.LoginComplete.as_view(),name="login-complete"), 26 | path("logout", keycloak_views.Logout.as_view(), name='keycloak_logout'), 27 | 28 | # Your stuff: custom urls includes go here 29 | ] + static( 30 | settings.MEDIA_URL, document_root=settings.MEDIA_ROOT 31 | ) 32 | 33 | if settings.DEBUG: 34 | # This allows the error pages to be debugged during development, just visit 35 | # these url in browser to see how these error pages look like. 36 | urlpatterns += [ 37 | path( 38 | "400/", 39 | default_views.bad_request, 40 | kwargs={"exception": Exception("Bad Request!")}, 41 | ), 42 | path( 43 | "403/", 44 | default_views.permission_denied, 45 | kwargs={"exception": Exception("Permission Denied")}, 46 | ), 47 | path( 48 | "404/", 49 | default_views.page_not_found, 50 | kwargs={"exception": Exception("Page not Found")}, 51 | ), 52 | path("500/", default_views.server_error), 53 | ] 54 | if "debug_toolbar" in settings.INSTALLED_APPS: 55 | import debug_toolbar 56 | 57 | urlpatterns = [path("__debug__/", include(debug_toolbar.urls))] + urlpatterns 58 | -------------------------------------------------------------------------------- /config/settings/test.py: -------------------------------------------------------------------------------- 1 | """ 2 | With these settings, tests run faster. 3 | """ 4 | 5 | from .base import * # noqa 6 | from .base import env 7 | 8 | # GENERAL 9 | # ------------------------------------------------------------------------------ 10 | # https://docs.djangoproject.com/en/dev/ref/settings/#debug 11 | DEBUG = False 12 | # https://docs.djangoproject.com/en/dev/ref/settings/#secret-key 13 | SECRET_KEY = env("DJANGO_SECRET_KEY", default="dGeObIwNzjEB0hI5u307R2AgsmycDH6bAUQLuVy14hrq5veydZt9YT1CksDf0EyM") 14 | # https://docs.djangoproject.com/en/dev/ref/settings/#test-runner 15 | TEST_RUNNER = "django.test.runner.DiscoverRunner" 16 | 17 | # CACHES 18 | # ------------------------------------------------------------------------------ 19 | # https://docs.djangoproject.com/en/dev/ref/settings/#caches 20 | CACHES = { 21 | "default": { 22 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", "LOCATION": "" 23 | } 24 | } 25 | 26 | # PASSWORDS 27 | # ------------------------------------------------------------------------------ 28 | # https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers 29 | PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"] 30 | 31 | # TEMPLATES 32 | # ------------------------------------------------------------------------------ 33 | # https://docs.djangoproject.com/en/dev/ref/settings/#templates 34 | TEMPLATES[0]["OPTIONS"]["debug"] = DEBUG # noqa F405 35 | TEMPLATES[0]["OPTIONS"]["loaders"] = [ # noqa F405 36 | ( 37 | "django.template.loaders.cached.Loader", 38 | [ 39 | "django.template.loaders.filesystem.Loader", 40 | "django.template.loaders.app_directories.Loader", 41 | ], 42 | ) 43 | ] 44 | 45 | # EMAIL 46 | # ------------------------------------------------------------------------------ 47 | # https://docs.djangoproject.com/en/dev/ref/settings/#email-backend 48 | EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend" 49 | # https://docs.djangoproject.com/en/dev/ref/settings/#email-host 50 | EMAIL_HOST = "localhost" 51 | # https://docs.djangoproject.com/en/dev/ref/settings/#email-port 52 | EMAIL_PORT = 1025 53 | 54 | # Your stuff... 55 | # ------------------------------------------------------------------------------ 56 | -------------------------------------------------------------------------------- /merge_production_dotenvs_in_dotenv.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Sequence 3 | 4 | import pytest 5 | 6 | ROOT_DIR_PATH = os.path.dirname(os.path.realpath(__file__)) 7 | PRODUCTION_DOTENVS_DIR_PATH = os.path.join(ROOT_DIR_PATH, ".envs", ".production") 8 | PRODUCTION_DOTENV_FILE_PATHS = [ 9 | os.path.join(PRODUCTION_DOTENVS_DIR_PATH, ".django"), 10 | os.path.join(PRODUCTION_DOTENVS_DIR_PATH, ".postgres"), 11 | os.path.join(PRODUCTION_DOTENVS_DIR_PATH, ".caddy"), 12 | ] 13 | DOTENV_FILE_PATH = os.path.join(ROOT_DIR_PATH, ".env") 14 | 15 | 16 | def merge( 17 | output_file_path: str, merged_file_paths: Sequence[str], append_linesep: bool = True 18 | ) -> None: 19 | with open(output_file_path, "w") as output_file: 20 | for merged_file_path in merged_file_paths: 21 | with open(merged_file_path, "r") as merged_file: 22 | merged_file_content = merged_file.read() 23 | output_file.write(merged_file_content) 24 | if append_linesep: 25 | output_file.write(os.linesep) 26 | 27 | 28 | def main(): 29 | merge(DOTENV_FILE_PATH, PRODUCTION_DOTENV_FILE_PATHS) 30 | 31 | 32 | @pytest.mark.parametrize("merged_file_count", range(3)) 33 | @pytest.mark.parametrize("append_linesep", [True, False]) 34 | def test_merge(tmpdir_factory, merged_file_count: int, append_linesep: bool): 35 | tmp_dir_path = str(tmpdir_factory.getbasetemp()) 36 | 37 | output_file_path = os.path.join(tmp_dir_path, ".env") 38 | 39 | expected_output_file_content = "" 40 | merged_file_paths = [] 41 | for i in range(merged_file_count): 42 | merged_file_ord = i + 1 43 | 44 | merged_filename = ".service{}".format(merged_file_ord) 45 | merged_file_path = os.path.join(tmp_dir_path, merged_filename) 46 | 47 | merged_file_content = merged_filename * merged_file_ord 48 | 49 | with open(merged_file_path, "w+") as file: 50 | file.write(merged_file_content) 51 | 52 | expected_output_file_content += merged_file_content 53 | if append_linesep: 54 | expected_output_file_content += os.linesep 55 | 56 | merged_file_paths.append(merged_file_path) 57 | 58 | merge(output_file_path, merged_file_paths, append_linesep) 59 | 60 | with open(output_file_path, "r") as output_file: 61 | actual_output_file_content = output_file.read() 62 | 63 | assert actual_output_file_content == expected_output_file_content 64 | 65 | 66 | if __name__ == "__main__": 67 | main() 68 | -------------------------------------------------------------------------------- /utility/install_os_dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | WORK_DIR="$(dirname "$0")" 4 | DISTRO_NAME=$(lsb_release -sc) 5 | OS_REQUIREMENTS_FILENAME="requirements-$DISTRO_NAME.apt" 6 | 7 | cd $WORK_DIR 8 | 9 | # Check if a requirements file exist for the current distribution. 10 | if [ ! -r "$OS_REQUIREMENTS_FILENAME" ]; then 11 | cat <<-EOF >&2 12 | There is no requirements file for your distribution. 13 | You can see one of the files listed below to help search the equivalent package in your system: 14 | $(find ./ -name "requirements-*.apt" -printf " - %f\n") 15 | EOF 16 | exit 1; 17 | fi 18 | 19 | # Handle call with wrong command 20 | function wrong_command() 21 | { 22 | echo "${0##*/} - unknown command: '${1}'" >&2 23 | usage_message 24 | } 25 | 26 | # Print help / script usage 27 | function usage_message() 28 | { 29 | cat <<-EOF 30 | Usage: $WORK_DIR/${0##*/} 31 | Available commands are: 32 | list Print a list of all packages defined on ${OS_REQUIREMENTS_FILENAME} file 33 | help Print this help 34 | 35 | Commands that require superuser permission: 36 | install Install packages defined on ${OS_REQUIREMENTS_FILENAME} file. Note: This 37 | does not upgrade the packages already installed for new versions, even if 38 | new version is available in the repository. 39 | upgrade Same that install, but upgrade the already installed packages, if new 40 | version is available. 41 | EOF 42 | } 43 | 44 | # Read the requirements.apt file, and remove comments and blank lines 45 | function list_packages(){ 46 | grep -v "#" "${OS_REQUIREMENTS_FILENAME}" | grep -v "^$"; 47 | } 48 | 49 | function install_packages() 50 | { 51 | list_packages | xargs apt-get --no-upgrade install -y; 52 | } 53 | 54 | function upgrade_packages() 55 | { 56 | list_packages | xargs apt-get install -y; 57 | } 58 | 59 | function install_or_upgrade() 60 | { 61 | P=${1} 62 | PARAN=${P:-"install"} 63 | 64 | if [[ $EUID -ne 0 ]]; then 65 | cat <<-EOF >&2 66 | You must run this script with root privilege 67 | Please do: 68 | sudo $WORK_DIR/${0##*/} $PARAN 69 | EOF 70 | exit 1 71 | else 72 | 73 | apt-get update 74 | 75 | # Install the basic compilation dependencies and other required libraries of this project 76 | if [ "$PARAN" == "install" ]; then 77 | install_packages; 78 | else 79 | upgrade_packages; 80 | fi 81 | 82 | # cleaning downloaded packages from apt-get cache 83 | apt-get clean 84 | 85 | exit 0 86 | fi 87 | } 88 | 89 | # Handle command argument 90 | case "$1" in 91 | install) install_or_upgrade;; 92 | upgrade) install_or_upgrade "upgrade";; 93 | list) list_packages;; 94 | help|"") usage_message;; 95 | *) wrong_command "$1";; 96 | esac 97 | -------------------------------------------------------------------------------- /demoapp/templates/account/email.html: -------------------------------------------------------------------------------- 1 | 2 | {% extends "account/base.html" %} 3 | 4 | {% load i18n %} 5 | {% load crispy_forms_tags %} 6 | 7 | {% block head_title %}{% trans "Account" %}{% endblock %} 8 | 9 | {% block inner %} 10 |

{% trans "E-mail Addresses" %}

11 | 12 | {% if user.emailaddress_set.all %} 13 |

{% trans 'The following e-mail addresses are associated with your account:' %}

14 | 15 | 44 | 45 | {% else %} 46 |

{% trans 'Warning:'%} {% trans "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}

47 | 48 | {% endif %} 49 | 50 | 51 |

{% trans "Add E-mail Address" %}

52 | 53 |
54 | {% csrf_token %} 55 | {{ form|crispy }} 56 | 57 |
58 | 59 | {% endblock %} 60 | 61 | 62 | {% block javascript %} 63 | {{ block.super }} 64 | 79 | {% endblock %} 80 | 81 | -------------------------------------------------------------------------------- /config/settings/local.py: -------------------------------------------------------------------------------- 1 | from .base import * # noqa 2 | from .base import env 3 | 4 | # GENERAL 5 | # ------------------------------------------------------------------------------ 6 | # https://docs.djangoproject.com/en/dev/ref/settings/#debug 7 | DEBUG = True 8 | # https://docs.djangoproject.com/en/dev/ref/settings/#secret-key 9 | SECRET_KEY = env('DJANGO_SECRET_KEY', default='duqzzAHzNonIcKrtMeKlQjc5eZ1bF5iXRh8QIw2dz9dCLuO25qKSWqDmmrfUjHqN') 10 | # https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts 11 | # ALLOWED_HOSTS = [ 12 | # "localhost", 13 | # "0.0.0.0", 14 | # "127.0.0.1", 15 | # "13.127.112.133", 16 | # "192.168.1.47", 17 | # "nginx", 18 | # ] 19 | 20 | ALLOWED_HOSTS = ['*'] 21 | # CACHES 22 | # ------------------------------------------------------------------------------ 23 | # https://docs.djangoproject.com/en/dev/ref/settings/#caches 24 | CACHES = { 25 | 'default': { 26 | 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 27 | 'LOCATION': '' 28 | } 29 | } 30 | 31 | # TEMPLATES 32 | # ------------------------------------------------------------------------------ 33 | # https://docs.djangoproject.com/en/dev/ref/settings/#templates 34 | TEMPLATES[0]['OPTIONS']['debug'] = DEBUG # noqa F405 35 | 36 | # EMAIL 37 | # ------------------------------------------------------------------------------ 38 | # https://docs.djangoproject.com/en/dev/ref/settings/#email-host 39 | EMAIL_HOST = env('EMAIL_HOST', default='mailhog') 40 | # https://docs.djangoproject.com/en/dev/ref/settings/#email-port 41 | EMAIL_PORT = 1025 42 | 43 | # django-debug-toolbar 44 | # ------------------------------------------------------------------------------ 45 | # https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites 46 | INSTALLED_APPS += ['debug_toolbar'] # noqa F405 47 | # https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware 48 | MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware'] # noqa F405 49 | # https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-config 50 | DEBUG_TOOLBAR_CONFIG = { 51 | 'DISABLE_PANELS': [ 52 | 'debug_toolbar.panels.redirects.RedirectsPanel', 53 | ], 54 | 'SHOW_TEMPLATE_CONTEXT': True, 55 | } 56 | # https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#internal-ips 57 | INTERNAL_IPS = ['127.0.0.1', '10.0.2.2'] 58 | if env('USE_DOCKER') == 'yes': 59 | import socket 60 | hostname, _, ips = socket.gethostbyname_ex(socket.gethostname()) 61 | INTERNAL_IPS += [ip[:-1] + '1' for ip in ips] 62 | 63 | # django-extensions 64 | # ------------------------------------------------------------------------------ 65 | # https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration 66 | INSTALLED_APPS += ['django_extensions'] # noqa F405 67 | 68 | # Your stuff... 69 | # ------------------------------------------------------------------------------ 70 | -------------------------------------------------------------------------------- /superset-conf/custom-css-templates/blue-dashboard.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background: white; 3 | font-family: Courier, Monaco, monospace; 4 | } 5 | 6 | .btn-default{ 7 | border-color: #3182bd; 8 | } 9 | 10 | .btn-default:active:hover, .btn-default.active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:active:focus, .btn-default.active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:active.focus, .btn-default.active.focus, .open > .dropdown-toggle.btn-default.focus { 11 | background-color: white; 12 | } 13 | 14 | .btn-default:hover{ 15 | color: #3182bd; 16 | background-color: white; 17 | } 18 | 19 | /* navbar */ 20 | 21 | .navbar { 22 | box-shadow: none; 23 | } 24 | 25 | .navbar { 26 | transition: opacity 0.5s ease; 27 | opacity: 1; 28 | } 29 | 30 | .navbar:hover { 31 | opacity: 1; 32 | } 33 | 34 | .navbar-inverse .navbar-nav > li > a{ 35 | border-bottom-color: #3182bd !important; 36 | } 37 | 38 | .navbar-inverse .navbar-nav > li > a:hover{ 39 | border-bottom-color: #3182bd; 40 | color:#3182bd; 41 | } 42 | 43 | .navbar-inverse .navbar-nav > .open > a:focus { 44 | background-color: white; 45 | } 46 | 47 | .dropdown-menu > li > a:hover{ 48 | color:#3182bd; 49 | background: white; 50 | } 51 | 52 | /* chart */ 53 | .chart-header .header{ 54 | font-weight: normal; 55 | font-size: 12px; 56 | color: white !important; 57 | } 58 | 59 | .gridster div.widget { 60 | transition: background-color 0.5s ease; 61 | background-color: #3182bd; 62 | border: 2px solid white; 63 | border-radius: 15px; 64 | box-shadow: none; 65 | } 66 | 67 | .gridster div.widget:hover { 68 | 69 | } 70 | 71 | .pull-right a{ 72 | color: white; 73 | } 74 | 75 | /* text */ 76 | 77 | h2 { 78 | color: white; 79 | font-size: 52px; 80 | } 81 | 82 | h1{ 83 | color:#3182bd; 84 | } 85 | 86 | .nvd3 text { 87 | font-size: 12px; 88 | font-family: inherit; 89 | } 90 | 91 | /* graph */ 92 | 93 | /* tables */ 94 | 95 | table { 96 | color: #3182bd !important; 97 | } 98 | 99 | .slice_container:hover{ 100 | border-top-width:0px; 101 | border-right-width:1px; 102 | border-left-width:1px; 103 | border-bottom-width:1px; 104 | border-style: solid; 105 | border-radius: 0 0px 15px 15px; 106 | border-color: #3182bd; 107 | } 108 | 109 | /* world graph */ 110 | 111 | .datamaps-bubble { 112 | fill:#3182bd !important; 113 | stroke:white !important; 114 | } 115 | 116 | .datamaps-hoverover { 117 | box-shadow: 1px 1px 5px #3182bd !important; 118 | color: #3182bd; 119 | } 120 | 121 | .hoverinfo { 122 | padding: 4px; 123 | border-radius: 1px; 124 | box-shadow: 1px 1px 5px #3182bd; 125 | font-size: 12px; 126 | border: 1px solid #CCC; 127 | } 128 | 129 | .datamaps-subunit:hover{ 130 | fill: #3182bd !important; 131 | } 132 | 133 | .datamaps-subunit:not(:hover){ 134 | fill: #ccc !important; 135 | } 136 | 137 | /**/ 138 | 139 | text.big{ 140 | stroke: white !important; 141 | fill: #3182bd !important; 142 | } 143 | -------------------------------------------------------------------------------- /superset-conf/custom-css-templates/red-dashboard.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background: white; 3 | font-family: Courier, Monaco, monospace; 4 | } 5 | 6 | .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { 7 | text-decoration: none; 8 | color: white !important; 9 | background-color: red !important; 10 | } 11 | 12 | .btn-default{ 13 | border-color: red; 14 | } 15 | 16 | .btn-default:active:hover, .btn-default.active:hover, .open > .dropdown-toggle.btn-default:hover, .btn-default:active:focus, .btn-default.active:focus, .open > .dropdown-toggle.btn-default:focus, .btn-default:active.focus, .btn-default.active.focus, .open > .dropdown-toggle.btn-default.focus { 17 | background-color: red; 18 | color: white; 19 | border-color: red; 20 | } 21 | 22 | .btn-default:hover{ 23 | color: white !important; 24 | background-color: red; 25 | border-color: red; 26 | } 27 | 28 | /* navbar */ 29 | 30 | .navbar { 31 | box-shadow: none; 32 | } 33 | 34 | .navbar { 35 | transition: opacity 0.5s ease; 36 | opacity: 1; 37 | } 38 | 39 | .navbar:hover { 40 | opacity: 1; 41 | } 42 | 43 | .navbar-inverse .navbar-nav > li > a{ 44 | border-bottom-color: red !important; 45 | } 46 | 47 | .navbar-inverse .navbar-nav > li > a:hover{ 48 | border-bottom-color: red; 49 | color:red; 50 | } 51 | 52 | .navbar-inverse .navbar-nav > .open > a:focus { 53 | color:red; 54 | background-color: white; 55 | } 56 | 57 | .dropdown-menu > li > a:hover{ 58 | color:red; 59 | background: white; 60 | } 61 | 62 | 63 | /* chart */ 64 | .chart-header .header{ 65 | font-weight: normal; 66 | font-size: 12px; 67 | color: white !important; 68 | } 69 | 70 | .gridster div.widget { 71 | transition: background-color 0.5s ease; 72 | background-color: red; 73 | border: 2px solid white; 74 | border-radius: 15px; 75 | box-shadow: none; 76 | } 77 | 78 | .gridster div.widget:hover { 79 | 80 | } 81 | 82 | .pull-right a{ 83 | color: white; 84 | } 85 | 86 | /* text */ 87 | 88 | h2 { 89 | color: white; 90 | font-size: 52px; 91 | } 92 | 93 | h1{ 94 | color:red; 95 | } 96 | 97 | .nvd3 text { 98 | font-size: 12px; 99 | font-family: inherit; 100 | } 101 | 102 | /* graph */ 103 | 104 | /* tables */ 105 | 106 | table { 107 | color: red !important; 108 | } 109 | 110 | .slice_container:hover{ 111 | border-top-width:0px; 112 | border-right-width:1px; 113 | border-left-width:1px; 114 | border-bottom-width:1px; 115 | border-style: solid; 116 | border-radius: 0 0px 15px 15px; 117 | border-color: red; 118 | } 119 | 120 | /* world graph */ 121 | 122 | .datamaps-bubble { 123 | fill:red !important; 124 | stroke:white !important; 125 | } 126 | 127 | .datamaps-hoverover { 128 | box-shadow: 1px 1px 5px red !important; 129 | color: red; 130 | } 131 | 132 | .hoverinfo { 133 | padding: 4px; 134 | border-radius: 1px; 135 | box-shadow: 1px 1px 5px red; 136 | font-size: 12px; 137 | border: 1px solid #CCC; 138 | } 139 | 140 | .datamaps-subunit:hover{ 141 | fill: red !important; 142 | } 143 | 144 | .datamaps-subunit:not(:hover){ 145 | fill: #ccc !important; 146 | } 147 | 148 | /**/ 149 | 150 | text.big{ 151 | stroke: white !important; 152 | fill: red !important; 153 | } 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | DemoApp 2 | ======= 3 | 4 | Demostrate django app which can integrate with Apache Superset 5 | 6 | Settings 7 | -------- 8 | 9 | Moved to settings_. 10 | 11 | .. _settings: http://cookiecutter-django.readthedocs.io/en/latest/settings.html 12 | 13 | Getting started 14 | -------------- 15 | 16 | This application development is done within Docker containers. It should be quick to get up and running with the application. 17 | 18 | `docker-compose -f local.yml up` 19 | DB migration is already taken care of., So no need to execute them. 20 | 21 | On a different terminal, execute management commands: 22 | ```` 23 | docker-compose -f local.yml run --rm django python manage.py createsuperuser 24 | #django is the django app container name 25 | ```` 26 | 27 | Setting Up Your Users 28 | --- 29 | 30 | * To create a **normal user account**, just go to Sign Up and fill out the form. Once you submit it, you'll see a "Verify Your E-mail Address" page. Go to your console to see a simulated email verification message. Copy the link into your browser. Now the user's email should be verified and ready to go. 31 | 32 | * To create an **superuser account**, use this command:: 33 | 34 | $ python manage.py createsuperuser 35 | 36 | For convenience, you can keep your normal user logged in on Chrome and your superuser logged in on Firefox (or similar), so that you can see how the site behaves for both kinds of users. 37 | 38 | Superset database initialization 39 | --- 40 | 41 | On AWS EC2 or linux instance, need to grant write access to superset data directory 42 | `chmod 777 superset-conf/data` 43 | 44 | Superset needs to initialize the database and create admin user account. Please execute the following command 45 | 46 | `docker exec -it django-demo-project_superset_1 superset-init` 47 | 48 | django-demo-project_superset_1 - this is name generated automatically based on our local.yml 49 | 50 | If you like to load examples: 51 | `docker exec -it django-demo-project_superset_1 superset load_examples` 52 | 53 | #### Connecting superset with external postgres 54 | 55 | 56 | In datasources, add following SQLAlchemy URI : 57 | `postgresql://debug:debug@postgres:5432/demoapp` 58 | 59 | ##### Command line connection to docker postgres 60 | `psql -h localhost -U debug -d demoapp` 61 | 62 | 63 | Test coverage 64 | --- 65 | 66 | To run the tests, check your test coverage, and generate an HTML coverage report:: 67 | 68 | $ coverage run manage.py test 69 | $ coverage html 70 | $ open htmlcov/index.html 71 | 72 | Running tests with py.test 73 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 74 | 75 | :: 76 | 77 | $ py.test 78 | 79 | Live reloading and Sass CSS compilation 80 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 81 | 82 | Moved to `Live reloading and SASS compilation`_. 83 | 84 | .. _`Live reloading and SASS compilation`: http://cookiecutter-django.readthedocs.io/en/latest/live-reloading-and-sass-compilation.html 85 | 86 | 87 | 88 | 89 | Email Server 90 | ^^^^^^^^^^^^ 91 | 92 | In development, it is often nice to be able to see emails that are being sent from your application. For that reason local SMTP server `MailHog`_ with a web interface is available as docker container. 93 | 94 | Container mailhog will start automatically when you will run all docker containers. 95 | Please check `cookiecutter-django Docker documentation`_ for more details how to start all containers. 96 | 97 | With MailHog running, to view messages that are sent by your application, open your browser and go to ``http://127.0.0.1:8025`` 98 | 99 | .. _mailhog: https://github.com/mailhog/MailHog 100 | 101 | 102 | 103 | Deployment 104 | ---------- 105 | 106 | The following details how to deploy this application. 107 | 108 | 109 | 110 | Docker 111 | ^^^^^^ 112 | 113 | See detailed `cookiecutter-django Docker documentation`_. 114 | 115 | .. _`cookiecutter-django Docker documentation`: http://cookiecutter-django.readthedocs.io/en/latest/deployment-with-docker.html 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Python template 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | staticfiles/ 55 | 56 | # Sphinx documentation 57 | docs/_build/ 58 | 59 | # PyBuilder 60 | target/ 61 | 62 | # pyenv 63 | .python-version 64 | 65 | 66 | 67 | # Environments 68 | .venv 69 | venv/ 70 | ENV/ 71 | 72 | # Rope project settings 73 | .ropeproject 74 | 75 | # mkdocs documentation 76 | /site 77 | 78 | # mypy 79 | .mypy_cache/ 80 | 81 | 82 | ### Node template 83 | # Logs 84 | logs 85 | *.log 86 | npm-debug.log* 87 | yarn-debug.log* 88 | yarn-error.log* 89 | 90 | # Runtime data 91 | pids 92 | *.pid 93 | *.seed 94 | *.pid.lock 95 | 96 | # Directory for instrumented libs generated by jscoverage/JSCover 97 | lib-cov 98 | 99 | # Coverage directory used by tools like istanbul 100 | coverage 101 | 102 | # nyc test coverage 103 | .nyc_output 104 | 105 | # Bower dependency directory (https://bower.io/) 106 | bower_components 107 | 108 | # node-waf configuration 109 | .lock-wscript 110 | 111 | # Compiled binary addons (http://nodejs.org/api/addons.html) 112 | build/Release 113 | 114 | # Dependency directories 115 | node_modules/ 116 | jspm_packages/ 117 | 118 | # Typescript v1 declaration files 119 | typings/ 120 | 121 | # Optional npm cache directory 122 | .npm 123 | 124 | # Optional eslint cache 125 | .eslintcache 126 | 127 | # Optional REPL history 128 | .node_repl_history 129 | 130 | # Output of 'npm pack' 131 | *.tgz 132 | 133 | # Yarn Integrity file 134 | .yarn-integrity 135 | 136 | 137 | ### Linux template 138 | *~ 139 | 140 | # temporary files which can be created if a process still has a handle open of a deleted file 141 | .fuse_hidden* 142 | 143 | # KDE directory preferences 144 | .directory 145 | 146 | # Linux trash folder which might appear on any partition or disk 147 | .Trash-* 148 | 149 | # .nfs files are created when an open file is removed but is still being accessed 150 | .nfs* 151 | 152 | 153 | ### VisualStudioCode template 154 | .vscode/* 155 | !.vscode/settings.json 156 | !.vscode/tasks.json 157 | !.vscode/launch.json 158 | !.vscode/extensions.json 159 | 160 | 161 | 162 | 163 | 164 | ### Windows template 165 | # Windows thumbnail cache files 166 | Thumbs.db 167 | ehthumbs.db 168 | ehthumbs_vista.db 169 | 170 | # Dump file 171 | *.stackdump 172 | 173 | # Folder config file 174 | Desktop.ini 175 | 176 | # Recycle Bin used on file shares 177 | $RECYCLE.BIN/ 178 | 179 | # Windows Installer files 180 | *.cab 181 | *.msi 182 | *.msm 183 | *.msp 184 | 185 | # Windows shortcuts 186 | *.lnk 187 | 188 | 189 | ### macOS template 190 | # General 191 | *.DS_Store 192 | .AppleDouble 193 | .LSOverride 194 | 195 | # Icon must end with two \r 196 | Icon 197 | 198 | # Thumbnails 199 | ._* 200 | 201 | # Files that might appear in the root of a volume 202 | .DocumentRevisions-V100 203 | .fseventsd 204 | .Spotlight-V100 205 | .TemporaryItems 206 | .Trashes 207 | .VolumeIcon.icns 208 | .com.apple.timemachine.donotpresent 209 | 210 | # Directories potentially created on remote AFP share 211 | .AppleDB 212 | .AppleDesktop 213 | Network Trash Folder 214 | Temporary Items 215 | .apdisk 216 | 217 | 218 | ### SublimeText template 219 | # Cache files for Sublime Text 220 | *.tmlanguage.cache 221 | *.tmPreferences.cache 222 | *.stTheme.cache 223 | 224 | # Workspace files are user-specific 225 | *.sublime-workspace 226 | 227 | # Project files should be checked into the repository, unless a significant 228 | # proportion of contributors will probably not be using Sublime Text 229 | # *.sublime-project 230 | 231 | # SFTP configuration file 232 | sftp-config.json 233 | 234 | # Package control specific files 235 | Package Control.last-run 236 | Package Control.ca-list 237 | Package Control.ca-bundle 238 | Package Control.system-ca-bundle 239 | Package Control.cache/ 240 | Package Control.ca-certs/ 241 | Package Control.merged-ca-bundle 242 | Package Control.user-ca-bundle 243 | oscrypto-ca-bundle.crt 244 | bh_unicode_properties.cache 245 | 246 | # Sublime-github package stores a github token in this file 247 | # https://packagecontrol.io/packages/sublime-github 248 | GitHub.sublime-settings 249 | 250 | 251 | ### Vim template 252 | # Swap 253 | [._]*.s[a-v][a-z] 254 | [._]*.sw[a-p] 255 | [._]s[a-v][a-z] 256 | [._]sw[a-p] 257 | 258 | # Session 259 | Session.vim 260 | 261 | # Temporary 262 | .netrwhist 263 | 264 | # Auto-generated tag files 265 | tags 266 | 267 | 268 | ### Project template 269 | 270 | demoapp/media/ 271 | .env 272 | .envs/* 273 | !.envs/.local/ 274 | -------------------------------------------------------------------------------- /demoapp/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load static i18n %} 2 | 3 | 4 | 5 | 6 | {% block title %}DemoApp{% endblock title %} 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | {% block css %} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {% endblock %} 32 | 33 | 34 | 35 | 36 | 37 |
38 | 77 | 78 |
79 | 80 |
81 | 82 | {% if messages %} 83 | {% for message in messages %} 84 |
{{ message }}
85 | {% endfor %} 86 | {% endif %} 87 | 88 | {% block content %} 89 |

Use this document as a way to quick start any new project.

90 | {% endblock content %} 91 | 92 |
93 | 94 | {% block modal %}{% endblock modal %} 95 | 96 | 98 | 99 | {% block javascript %} 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | {% endblock javascript %} 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /demoapp/users/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | import django.contrib.auth.models 2 | import django.contrib.auth.validators 3 | from django.db import migrations, models 4 | import django.utils.timezone 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | initial = True 10 | 11 | dependencies = [("auth", "0008_alter_user_username_max_length")] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name="User", 16 | fields=[ 17 | ( 18 | "id", 19 | models.AutoField( 20 | auto_created=True, 21 | primary_key=True, 22 | serialize=False, 23 | verbose_name="ID", 24 | ), 25 | ), 26 | ("password", models.CharField(max_length=128, verbose_name="password")), 27 | ( 28 | "last_login", 29 | models.DateTimeField( 30 | blank=True, null=True, verbose_name="last login" 31 | ), 32 | ), 33 | ( 34 | "is_superuser", 35 | models.BooleanField( 36 | default=False, 37 | help_text="Designates that this user has all permissions without explicitly assigning them.", 38 | verbose_name="superuser status", 39 | ), 40 | ), 41 | ( 42 | "username", 43 | models.CharField( 44 | error_messages={ 45 | "unique": "A user with that username already exists." 46 | }, 47 | help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", 48 | max_length=150, 49 | unique=True, 50 | validators=[ 51 | django.contrib.auth.validators.UnicodeUsernameValidator() 52 | ], 53 | verbose_name="username", 54 | ), 55 | ), 56 | ( 57 | "first_name", 58 | models.CharField( 59 | blank=True, max_length=30, verbose_name="first name" 60 | ), 61 | ), 62 | ( 63 | "last_name", 64 | models.CharField( 65 | blank=True, max_length=150, verbose_name="last name" 66 | ), 67 | ), 68 | ( 69 | "email", 70 | models.EmailField( 71 | blank=True, max_length=254, verbose_name="email address" 72 | ), 73 | ), 74 | ( 75 | "is_staff", 76 | models.BooleanField( 77 | default=False, 78 | help_text="Designates whether the user can log into this admin site.", 79 | verbose_name="staff status", 80 | ), 81 | ), 82 | ( 83 | "is_active", 84 | models.BooleanField( 85 | default=True, 86 | help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", 87 | verbose_name="active", 88 | ), 89 | ), 90 | ( 91 | "date_joined", 92 | models.DateTimeField( 93 | default=django.utils.timezone.now, verbose_name="date joined" 94 | ), 95 | ), 96 | ( 97 | "name", 98 | models.CharField( 99 | blank=True, max_length=255, verbose_name="Name of User" 100 | ), 101 | ), 102 | ( 103 | "groups", 104 | models.ManyToManyField( 105 | blank=True, 106 | help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", 107 | related_name="user_set", 108 | related_query_name="user", 109 | to="auth.Group", 110 | verbose_name="groups", 111 | ), 112 | ), 113 | ( 114 | "user_permissions", 115 | models.ManyToManyField( 116 | blank=True, 117 | help_text="Specific permissions for this user.", 118 | related_name="user_set", 119 | related_query_name="user", 120 | to="auth.Permission", 121 | verbose_name="user permissions", 122 | ), 123 | ), 124 | ], 125 | options={ 126 | "verbose_name_plural": "users", 127 | "verbose_name": "user", 128 | "abstract": False, 129 | }, 130 | managers=[("objects", django.contrib.auth.models.UserManager())], 131 | ) 132 | ] 133 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. linkcheck to check all external links for integrity 37 | echo. doctest to run all doctests embedded in the documentation if enabled 38 | goto end 39 | ) 40 | 41 | if "%1" == "clean" ( 42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 43 | del /q /s %BUILDDIR%\* 44 | goto end 45 | ) 46 | 47 | if "%1" == "html" ( 48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 49 | if errorlevel 1 exit /b 1 50 | echo. 51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 52 | goto end 53 | ) 54 | 55 | if "%1" == "dirhtml" ( 56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 57 | if errorlevel 1 exit /b 1 58 | echo. 59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 60 | goto end 61 | ) 62 | 63 | if "%1" == "singlehtml" ( 64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 68 | goto end 69 | ) 70 | 71 | if "%1" == "pickle" ( 72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished; now you can process the pickle files. 76 | goto end 77 | ) 78 | 79 | if "%1" == "json" ( 80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished; now you can process the JSON files. 84 | goto end 85 | ) 86 | 87 | if "%1" == "htmlhelp" ( 88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can run HTML Help Workshop with the ^ 92 | .hhp project file in %BUILDDIR%/htmlhelp. 93 | goto end 94 | ) 95 | 96 | if "%1" == "qthelp" ( 97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 98 | if errorlevel 1 exit /b 1 99 | echo. 100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 101 | .qhcp project file in %BUILDDIR%/qthelp, like this: 102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\demoapp.qhcp 103 | echo.To view the help file: 104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\demoapp.ghc 105 | goto end 106 | ) 107 | 108 | if "%1" == "devhelp" ( 109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 110 | if errorlevel 1 exit /b 1 111 | echo. 112 | echo.Build finished. 113 | goto end 114 | ) 115 | 116 | if "%1" == "epub" ( 117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 118 | if errorlevel 1 exit /b 1 119 | echo. 120 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 121 | goto end 122 | ) 123 | 124 | if "%1" == "latex" ( 125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 129 | goto end 130 | ) 131 | 132 | if "%1" == "text" ( 133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The text files are in %BUILDDIR%/text. 137 | goto end 138 | ) 139 | 140 | if "%1" == "man" ( 141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 145 | goto end 146 | ) 147 | 148 | if "%1" == "texinfo" ( 149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 150 | if errorlevel 1 exit /b 1 151 | echo. 152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 153 | goto end 154 | ) 155 | 156 | if "%1" == "gettext" ( 157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 158 | if errorlevel 1 exit /b 1 159 | echo. 160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 161 | goto end 162 | ) 163 | 164 | if "%1" == "changes" ( 165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 166 | if errorlevel 1 exit /b 1 167 | echo. 168 | echo.The overview file is in %BUILDDIR%/changes. 169 | goto end 170 | ) 171 | 172 | if "%1" == "linkcheck" ( 173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 174 | if errorlevel 1 exit /b 1 175 | echo. 176 | echo.Link check complete; look for any errors in the above output ^ 177 | or in %BUILDDIR%/linkcheck/output.txt. 178 | goto end 179 | ) 180 | 181 | if "%1" == "doctest" ( 182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 183 | if errorlevel 1 exit /b 1 184 | echo. 185 | echo.Testing of doctests in the sources finished, look at the ^ 186 | results in %BUILDDIR%/doctest/output.txt. 187 | goto end 188 | ) 189 | 190 | :end 191 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/demoapp.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/demoapp.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/demoapp" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/demoapp" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /docs/docker_ec2.rst: -------------------------------------------------------------------------------- 1 | Developing with Docker 2 | ====================== 3 | 4 | You can develop your application in a `Docker`_ container for simpler deployment onto bare Linux machines later. This instruction assumes an `Amazon Web Services`_ EC2 instance, but it should work on any machine with Docker > 1.3 and `Docker compose`_ installed. 5 | 6 | .. _Docker: https://www.docker.com/ 7 | .. _Amazon Web Services: http://aws.amazon.com/ 8 | .. _Docker compose: https://docs.docker.com/compose/ 9 | 10 | Setting up 11 | ^^^^^^^^^^ 12 | 13 | Docker encourages running one container for each process. This might mean one container for your web server, one for Django application and a third for your database. Once you're happy composing containers in this way you can easily add more, such as a `Redis`_ cache. 14 | 15 | .. _Redis: http://redis.io/ 16 | 17 | The Docker compose tool (previously known as `fig`_) makes linking these containers easy. An example set up for your Cookiecutter Django project might look like this: 18 | 19 | .. _fig: http://www.fig.sh/ 20 | 21 | :: 22 | 23 | webapp/ # Your cookiecutter project would be in here 24 | Dockerfile 25 | ... 26 | database/ 27 | Dockerfile 28 | ... 29 | webserver/ 30 | Dockerfile 31 | ... 32 | production.yml 33 | 34 | Each component of your application would get its own `Dockerfile`_. The rest of this example assumes you are using the `base postgres image`_ for your database. Your database settings in `config/base.py` might then look something like: 35 | 36 | .. _Dockerfile: https://docs.docker.com/reference/builder/ 37 | .. _base postgres image: https://registry.hub.docker.com/_/postgres/ 38 | 39 | .. code-block:: python 40 | 41 | DATABASES = { 42 | 'default': { 43 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 44 | 'NAME': 'postgres', 45 | 'USER': 'postgres', 46 | 'HOST': 'database', 47 | 'PORT': 5432, 48 | } 49 | } 50 | 51 | The `Docker compose documentation`_ explains in detail what you can accomplish in the `production.yml` file, but an example configuration might look like this: 52 | 53 | .. _Docker compose documentation: https://docs.docker.com/compose/#compose-documentation 54 | 55 | .. code-block:: yaml 56 | 57 | database: 58 | build: database 59 | webapp: 60 | build: webapp: 61 | command: /usr/bin/python3.6 manage.py runserver 0.0.0.0:8000 # dev setting 62 | # command: gunicorn -b 0.0.0.0:8000 wsgi:application # production setting 63 | volumes: 64 | - webapp/your_project_name:/path/to/container/workdir/ 65 | links: 66 | - database 67 | webserver: 68 | build: webserver 69 | ports: 70 | - "80:80" 71 | - "443:443" 72 | links: 73 | - webapp 74 | 75 | We'll ignore the webserver for now (you'll want to comment that part out while we do). A working Dockerfile to run your cookiecutter application might look like this: 76 | 77 | :: 78 | 79 | FROM ubuntu:14.04 80 | ENV REFRESHED_AT 2015-01-13 81 | 82 | # update packages and prepare to build software 83 | RUN ["apt-get", "update"] 84 | RUN ["apt-get", "-y", "install", "build-essential", "vim", "git", "curl"] 85 | RUN ["locale-gen", "en_GB.UTF-8"] 86 | 87 | # install latest python 88 | RUN ["apt-get", "-y", "build-dep", "python3-dev", "python3-imaging"] 89 | RUN ["apt-get", "-y", "install", "python3-dev", "python3-imaging", "python3-pip"] 90 | 91 | # prepare postgreSQL support 92 | RUN ["apt-get", "-y", "build-dep", "python3-psycopg2"] 93 | 94 | # move into our working directory 95 | # ADD must be after chown see http://stackoverflow.com/a/26145444/1281947 96 | RUN ["groupadd", "python"] 97 | RUN ["useradd", "python", "-s", "/bin/bash", "-m", "-g", "python", "-G", "python"] 98 | ENV HOME /home/python 99 | WORKDIR /home/python 100 | RUN ["chown", "-R", "python:python", "/home/python"] 101 | ADD ./ /home/python 102 | 103 | # manage requirements 104 | ENV REQUIREMENTS_REFRESHED_AT 2015-02-25 105 | RUN ["pip3", "install", "-r", "requirements.txt"] 106 | 107 | # uncomment the line below to use container as a non-root user 108 | USER python:python 109 | 110 | Running `sudo docker-compose -f production.yml build` will follow the instructions in your `production.yml` file and build the database container, then your webapp, before mounting your cookiecutter project files as a volume in the webapp container and linking to the database. Our example yaml file runs in development mode but changing it to production mode is as simple as commenting out the line using `runserver` and uncommenting the line using `gunicorn`. 111 | 112 | Both are set to run on port `0.0.0.0:8000`, which is where the Docker daemon will discover it. You can now run `sudo docker-compose -f production.yml up` and browse to `localhost:8000` to see your application running. 113 | 114 | Deployment 115 | ^^^^^^^^^^ 116 | 117 | You'll need a webserver container for deployment. An example setup for `Nginx`_ might look like this: 118 | 119 | .. _Nginx: http://wiki.nginx.org/Main 120 | 121 | :: 122 | 123 | FROM ubuntu:14.04 124 | ENV REFRESHED_AT 2015-02-11 125 | 126 | # get the nginx package and set it up 127 | RUN ["apt-get", "update"] 128 | RUN ["apt-get", "-y", "install", "nginx"] 129 | 130 | # forward request and error logs to docker log collector 131 | RUN ln -sf /dev/stdout /var/log/nginx/access.log 132 | RUN ln -sf /dev/stderr /var/log/nginx/error.log 133 | VOLUME ["/var/cache/nginx"] 134 | EXPOSE 80 443 135 | 136 | # load nginx conf 137 | ADD ./site.conf /etc/nginx/sites-available/your_cookiecutter_project 138 | RUN ["ln", "-s", "/etc/nginx/sites-available/your_cookiecutter_project", "/etc/nginx/sites-enabled/your_cookiecutter_project"] 139 | RUN ["rm", "-rf", "/etc/nginx/sites-available/default"] 140 | 141 | #start the server 142 | CMD ["nginx", "-g", "daemon off;"] 143 | 144 | That Dockerfile assumes you have an Nginx conf file named `site.conf` in the same directory as the webserver Dockerfile. A very basic example, which forwards traffic onto the development server or gunicorn for processing, would look like this: 145 | 146 | :: 147 | 148 | # see http://serverfault.com/questions/577370/how-can-i-use-environment-variables-in-nginx-conf#comment730384_577370 149 | upstream localhost { 150 | server webapp_1:8000; 151 | } 152 | server { 153 | location / { 154 | proxy_pass http://localhost; 155 | } 156 | } 157 | 158 | Running `sudo docker-compose -f production.yml build webserver` will build your server container. Running `sudo docker-compose -f production.yml up` will now expose your application directly on `localhost` (no need to specify the port number). 159 | 160 | Building and running your app on EC2 161 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 162 | 163 | All you now need to do to run your app in production is: 164 | 165 | * Create an empty EC2 Linux instance (any Linux machine should do). 166 | 167 | * Install your preferred source control solution, Docker and Docker compose on the news instance. 168 | 169 | * Pull in your code from source control. The root directory should be the one with your `production.yml` file in it. 170 | 171 | * Run `sudo docker-compose -f production.yml build` and `sudo docker-compose -f production.yml up`. 172 | 173 | * Assign an `Elastic IP address`_ to your new machine. 174 | 175 | .. _Elastic IP address: https://aws.amazon.com/articles/1346 176 | 177 | * Point your domain name to the elastic IP. 178 | 179 | **Be careful with Elastic IPs** because, on the AWS free tier, if you assign one and then stop the machine you will incur charges while the machine is down (presumably because you're preventing them allocating the IP to someone else). 180 | 181 | Security advisory 182 | ^^^^^^^^^^^^^^^^^ 183 | 184 | The setup described in this instruction will get you up-and-running but it hasn't been audited for security. If you are running your own setup like this it is always advisable to, at a minimum, examine your application with a tool like `OWASP ZAP`_ to see what security holes you might be leaving open. 185 | 186 | .. _OWASP ZAP: https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project 187 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # DemoApp documentation build configuration file, created by 2 | # sphinx-quickstart. 3 | # 4 | # This file is execfile()d with the current directory set to its containing dir. 5 | # 6 | # Note that not all possible configuration values are present in this 7 | # autogenerated file. 8 | # 9 | # All configuration values have a default; values that are commented out 10 | # serve to show the default. 11 | 12 | import os 13 | import sys 14 | 15 | # If extensions (or modules to document with autodoc) are in another directory, 16 | # add these directories to sys.path here. If the directory is relative to the 17 | # documentation root, use os.path.abspath to make it absolute, like shown here. 18 | # sys.path.insert(0, os.path.abspath('.')) 19 | 20 | # -- General configuration ----------------------------------------------------- 21 | 22 | # If your documentation needs a minimal Sphinx version, state it here. 23 | # needs_sphinx = '1.0' 24 | 25 | # Add any Sphinx extension module names here, as strings. They can be extensions 26 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 27 | extensions = [] 28 | 29 | # Add any paths that contain templates here, relative to this directory. 30 | templates_path = ["_templates"] 31 | 32 | # The suffix of source filenames. 33 | source_suffix = ".rst" 34 | 35 | # The encoding of source files. 36 | # source_encoding = 'utf-8-sig' 37 | 38 | # The master toctree document. 39 | master_doc = "index" 40 | 41 | # General information about the project. 42 | project = "DemoApp" 43 | copyright = """2018, Sairam Krish""" 44 | 45 | # The version info for the project you're documenting, acts as replacement for 46 | # |version| and |release|, also used in various other places throughout the 47 | # built documents. 48 | # 49 | # The short X.Y version. 50 | version = "0.1" 51 | # The full version, including alpha/beta/rc tags. 52 | release = "0.1" 53 | 54 | # The language for content autogenerated by Sphinx. Refer to documentation 55 | # for a list of supported languages. 56 | # language = None 57 | 58 | # There are two options for replacing |today|: either, you set today to some 59 | # non-false value, then it is used: 60 | # today = '' 61 | # Else, today_fmt is used as the format for a strftime call. 62 | # today_fmt = '%B %d, %Y' 63 | 64 | # List of patterns, relative to source directory, that match files and 65 | # directories to ignore when looking for source files. 66 | exclude_patterns = ["_build"] 67 | 68 | # The reST default role (used for this markup: `text`) to use for all documents. 69 | # default_role = None 70 | 71 | # If true, '()' will be appended to :func: etc. cross-reference text. 72 | # add_function_parentheses = True 73 | 74 | # If true, the current module name will be prepended to all description 75 | # unit titles (such as .. function::). 76 | # add_module_names = True 77 | 78 | # If true, sectionauthor and moduleauthor directives will be shown in the 79 | # output. They are ignored by default. 80 | # show_authors = False 81 | 82 | # The name of the Pygments (syntax highlighting) style to use. 83 | pygments_style = "sphinx" 84 | 85 | # A list of ignored prefixes for module index sorting. 86 | # modindex_common_prefix = [] 87 | 88 | 89 | # -- Options for HTML output --------------------------------------------------- 90 | 91 | # The theme to use for HTML and HTML Help pages. See the documentation for 92 | # a list of builtin themes. 93 | html_theme = "default" 94 | 95 | # Theme options are theme-specific and customize the look and feel of a theme 96 | # further. For a list of options available for each theme, see the 97 | # documentation. 98 | # html_theme_options = {} 99 | 100 | # Add any paths that contain custom themes here, relative to this directory. 101 | # html_theme_path = [] 102 | 103 | # The name for this set of Sphinx documents. If None, it defaults to 104 | # " v documentation". 105 | # html_title = None 106 | 107 | # A shorter title for the navigation bar. Default is the same as html_title. 108 | # html_short_title = None 109 | 110 | # The name of an image file (relative to this directory) to place at the top 111 | # of the sidebar. 112 | # html_logo = None 113 | 114 | # The name of an image file (within the static path) to use as favicon of the 115 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 116 | # pixels large. 117 | # html_favicon = None 118 | 119 | # Add any paths that contain custom static files (such as style sheets) here, 120 | # relative to this directory. They are copied after the builtin static files, 121 | # so a file named "default.css" will overwrite the builtin "default.css". 122 | html_static_path = ["_static"] 123 | 124 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 125 | # using the given strftime format. 126 | # html_last_updated_fmt = '%b %d, %Y' 127 | 128 | # If true, SmartyPants will be used to convert quotes and dashes to 129 | # typographically correct entities. 130 | # html_use_smartypants = True 131 | 132 | # Custom sidebar templates, maps document names to template names. 133 | # html_sidebars = {} 134 | 135 | # Additional templates that should be rendered to pages, maps page names to 136 | # template names. 137 | # html_additional_pages = {} 138 | 139 | # If false, no module index is generated. 140 | # html_domain_indices = True 141 | 142 | # If false, no index is generated. 143 | # html_use_index = True 144 | 145 | # If true, the index is split into individual pages for each letter. 146 | # html_split_index = False 147 | 148 | # If true, links to the reST sources are added to the pages. 149 | # html_show_sourcelink = True 150 | 151 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 152 | # html_show_sphinx = True 153 | 154 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 155 | # html_show_copyright = True 156 | 157 | # If true, an OpenSearch description file will be output, and all pages will 158 | # contain a tag referring to it. The value of this option must be the 159 | # base URL from which the finished HTML is served. 160 | # html_use_opensearch = '' 161 | 162 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 163 | # html_file_suffix = None 164 | 165 | # Output file base name for HTML help builder. 166 | htmlhelp_basename = "demoappdoc" 167 | 168 | 169 | # -- Options for LaTeX output -------------------------------------------------- 170 | 171 | latex_elements = { 172 | # The paper size ('letterpaper' or 'a4paper'). 173 | # 'papersize': 'letterpaper', 174 | # The font size ('10pt', '11pt' or '12pt'). 175 | # 'pointsize': '10pt', 176 | # Additional stuff for the LaTeX preamble. 177 | # 'preamble': '', 178 | } 179 | 180 | # Grouping the document tree into LaTeX files. List of tuples 181 | # (source start file, target name, title, author, documentclass [howto/manual]). 182 | latex_documents = [ 183 | ( 184 | "index", 185 | "demoapp.tex", 186 | "DemoApp Documentation", 187 | """Sairam Krish""", 188 | "manual", 189 | ) 190 | ] 191 | 192 | # The name of an image file (relative to this directory) to place at the top of 193 | # the title page. 194 | # latex_logo = None 195 | 196 | # For "manual" documents, if this is true, then toplevel headings are parts, 197 | # not chapters. 198 | # latex_use_parts = False 199 | 200 | # If true, show page references after internal links. 201 | # latex_show_pagerefs = False 202 | 203 | # If true, show URL addresses after external links. 204 | # latex_show_urls = False 205 | 206 | # Documents to append as an appendix to all manuals. 207 | # latex_appendices = [] 208 | 209 | # If false, no module index is generated. 210 | # latex_domain_indices = True 211 | 212 | 213 | # -- Options for manual page output -------------------------------------------- 214 | 215 | # One entry per manual page. List of tuples 216 | # (source start file, name, description, authors, manual section). 217 | man_pages = [ 218 | ( 219 | "index", 220 | "demoapp", 221 | "DemoApp Documentation", 222 | ["""Sairam Krish"""], 223 | 1, 224 | ) 225 | ] 226 | 227 | # If true, show URL addresses after external links. 228 | # man_show_urls = False 229 | 230 | 231 | # -- Options for Texinfo output ------------------------------------------------ 232 | 233 | # Grouping the document tree into Texinfo files. List of tuples 234 | # (source start file, target name, title, author, 235 | # dir menu entry, description, category) 236 | texinfo_documents = [ 237 | ( 238 | "index", 239 | "demoapp", 240 | "DemoApp Documentation", 241 | """Sairam Krish""", 242 | "DemoApp", 243 | """Demostrate django app which talks to superset""", 244 | "Miscellaneous", 245 | ) 246 | ] 247 | 248 | # Documents to append as an appendix to all manuals. 249 | # texinfo_appendices = [] 250 | 251 | # If false, no module index is generated. 252 | # texinfo_domain_indices = True 253 | 254 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 255 | # texinfo_show_urls = 'footnote' 256 | -------------------------------------------------------------------------------- /config/settings/production.py: -------------------------------------------------------------------------------- 1 | from .base import * # noqa 2 | from .base import env 3 | 4 | # GENERAL 5 | # ------------------------------------------------------------------------------ 6 | # https://docs.djangoproject.com/en/dev/ref/settings/#secret-key 7 | SECRET_KEY = env('DJANGO_SECRET_KEY') 8 | # https://docs.djangoproject.com/en/dev/ref/settings/#allowed-hosts 9 | ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS', default=['example.com']) 10 | 11 | # DATABASES 12 | # ------------------------------------------------------------------------------ 13 | DATABASES['default'] = env.db('DATABASE_URL') # noqa F405 14 | DATABASES['default']['ATOMIC_REQUESTS'] = True # noqa F405 15 | DATABASES['default']['CONN_MAX_AGE'] = env.int('CONN_MAX_AGE', default=60) # noqa F405 16 | 17 | # CACHES 18 | # ------------------------------------------------------------------------------ 19 | CACHES = { 20 | 'default': { 21 | 'BACKEND': 'django_redis.cache.RedisCache', 22 | 'LOCATION': env('REDIS_URL'), 23 | 'OPTIONS': { 24 | 'CLIENT_CLASS': 'django_redis.client.DefaultClient', 25 | # Mimicing memcache behavior. 26 | # http://niwinz.github.io/django-redis/latest/#_memcached_exceptions_behavior 27 | 'IGNORE_EXCEPTIONS': True, 28 | } 29 | } 30 | } 31 | 32 | # SECURITY 33 | # ------------------------------------------------------------------------------ 34 | # https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header 35 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 36 | # https://docs.djangoproject.com/en/dev/ref/settings/#secure-ssl-redirect 37 | SECURE_SSL_REDIRECT = env.bool('DJANGO_SECURE_SSL_REDIRECT', default=True) 38 | # https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-secure 39 | SESSION_COOKIE_SECURE = True 40 | # https://docs.djangoproject.com/en/dev/ref/settings/#session-cookie-httponly 41 | SESSION_COOKIE_HTTPONLY = True 42 | # https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-secure 43 | CSRF_COOKIE_SECURE = True 44 | # https://docs.djangoproject.com/en/dev/ref/settings/#csrf-cookie-httponly 45 | CSRF_COOKIE_HTTPONLY = True 46 | # https://docs.djangoproject.com/en/dev/topics/security/#ssl-https 47 | # https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-seconds 48 | # TODO: set this to 60 seconds first and then to 518400 once you prove the former works 49 | SECURE_HSTS_SECONDS = 60 50 | # https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-include-subdomains 51 | SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool('DJANGO_SECURE_HSTS_INCLUDE_SUBDOMAINS', default=True) 52 | # https://docs.djangoproject.com/en/dev/ref/settings/#secure-hsts-preload 53 | SECURE_HSTS_PRELOAD = env.bool('DJANGO_SECURE_HSTS_PRELOAD', default=True) 54 | # https://docs.djangoproject.com/en/dev/ref/middleware/#x-content-type-options-nosniff 55 | SECURE_CONTENT_TYPE_NOSNIFF = env.bool('DJANGO_SECURE_CONTENT_TYPE_NOSNIFF', default=True) 56 | # https://docs.djangoproject.com/en/dev/ref/settings/#secure-browser-xss-filter 57 | SECURE_BROWSER_XSS_FILTER = True 58 | # https://docs.djangoproject.com/en/dev/ref/settings/#x-frame-options 59 | X_FRAME_OPTIONS = 'DENY' 60 | 61 | # STORAGES 62 | # ------------------------------------------------------------------------------ 63 | # https://django-storages.readthedocs.io/en/latest/#installation 64 | INSTALLED_APPS += ['storages'] # noqa F405 65 | # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings 66 | AWS_ACCESS_KEY_ID = env('DJANGO_AWS_ACCESS_KEY_ID') 67 | # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings 68 | AWS_SECRET_ACCESS_KEY = env('DJANGO_AWS_SECRET_ACCESS_KEY') 69 | # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings 70 | AWS_STORAGE_BUCKET_NAME = env('DJANGO_AWS_STORAGE_BUCKET_NAME') 71 | # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings 72 | AWS_AUTO_CREATE_BUCKET = True 73 | # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings 74 | AWS_QUERYSTRING_AUTH = False 75 | # DO NOT change these unless you know what you're doing. 76 | _AWS_EXPIRY = 60 * 60 * 24 * 7 77 | # https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#settings 78 | AWS_S3_OBJECT_PARAMETERS = { 79 | 'CacheControl': f'max-age={_AWS_EXPIRY}, s-maxage={_AWS_EXPIRY}, must-revalidate', 80 | } 81 | 82 | # STATIC 83 | # ------------------------ 84 | 85 | STATICFILES_STORAGE = 'config.settings.production.StaticRootS3BotoStorage' 86 | STATIC_URL = f'https://s3.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}/static/' 87 | 88 | # MEDIA 89 | # ------------------------------------------------------------------------------ 90 | 91 | # region http://stackoverflow.com/questions/10390244/ 92 | from storages.backends.s3boto3 import S3Boto3Storage # noqa E402 93 | StaticRootS3BotoStorage = lambda: S3Boto3Storage(location='static') # noqa 94 | MediaRootS3BotoStorage = lambda: S3Boto3Storage(location='media', file_overwrite=False) # noqa 95 | # endregion 96 | DEFAULT_FILE_STORAGE = 'config.settings.production.MediaRootS3BotoStorage' 97 | MEDIA_URL = f'https://s3.amazonaws.com/{AWS_STORAGE_BUCKET_NAME}/media/' 98 | 99 | # TEMPLATES 100 | # ------------------------------------------------------------------------------ 101 | # https://docs.djangoproject.com/en/dev/ref/settings/#templates 102 | TEMPLATES[0]['OPTIONS']['loaders'] = [ # noqa F405 103 | ( 104 | 'django.template.loaders.cached.Loader', 105 | [ 106 | 'django.template.loaders.filesystem.Loader', 107 | 'django.template.loaders.app_directories.Loader', 108 | ] 109 | ), 110 | ] 111 | 112 | # EMAIL 113 | # ------------------------------------------------------------------------------ 114 | # https://docs.djangoproject.com/en/dev/ref/settings/#default-from-email 115 | DEFAULT_FROM_EMAIL = env( 116 | 'DJANGO_DEFAULT_FROM_EMAIL', 117 | default='DemoApp ' 118 | ) 119 | # https://docs.djangoproject.com/en/dev/ref/settings/#server-email 120 | SERVER_EMAIL = env('DJANGO_SERVER_EMAIL', default=DEFAULT_FROM_EMAIL) 121 | # https://docs.djangoproject.com/en/dev/ref/settings/#email-subject-prefix 122 | EMAIL_SUBJECT_PREFIX = env('DJANGO_EMAIL_SUBJECT_PREFIX', default='[DemoApp]') 123 | 124 | # ADMIN 125 | # ------------------------------------------------------------------------------ 126 | # Django Admin URL regex. 127 | ADMIN_URL = env('DJANGO_ADMIN_URL') 128 | 129 | # Anymail (Mailgun) 130 | # ------------------------------------------------------------------------------ 131 | # https://anymail.readthedocs.io/en/stable/installation/#installing-anymail 132 | INSTALLED_APPS += ['anymail'] # noqa F405 133 | EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend' 134 | # https://anymail.readthedocs.io/en/stable/installation/#anymail-settings-reference 135 | ANYMAIL = { 136 | 'MAILGUN_API_KEY': env('MAILGUN_API_KEY'), 137 | 'MAILGUN_SENDER_DOMAIN': env('MAILGUN_DOMAIN') 138 | } 139 | 140 | # Gunicorn 141 | # ------------------------------------------------------------------------------ 142 | INSTALLED_APPS += ['gunicorn'] # noqa F405 143 | 144 | # Collectfast 145 | # ------------------------------------------------------------------------------ 146 | # https://github.com/antonagestam/collectfast#installation 147 | INSTALLED_APPS = ['collectfast'] + INSTALLED_APPS # noqa F405 148 | AWS_PRELOAD_METADATA = True 149 | 150 | 151 | # LOGGING 152 | # ------------------------------------------------------------------------------ 153 | # See: https://docs.djangoproject.com/en/dev/ref/settings/#logging 154 | # A sample logging configuration. The only tangible logging 155 | # performed by this configuration is to send an email to 156 | # the site admins on every HTTP 500 error when DEBUG=False. 157 | # See https://docs.djangoproject.com/en/dev/topics/logging for 158 | # more details on how to customize your logging configuration. 159 | LOGGING = { 160 | 'version': 1, 161 | 'disable_existing_loggers': False, 162 | 'filters': { 163 | 'require_debug_false': { 164 | '()': 'django.utils.log.RequireDebugFalse' 165 | } 166 | }, 167 | 'formatters': { 168 | 'verbose': { 169 | 'format': '%(levelname)s %(asctime)s %(module)s ' 170 | '%(process)d %(thread)d %(message)s' 171 | }, 172 | }, 173 | 'handlers': { 174 | 'mail_admins': { 175 | 'level': 'ERROR', 176 | 'filters': ['require_debug_false'], 177 | 'class': 'django.utils.log.AdminEmailHandler' 178 | }, 179 | 'console': { 180 | 'level': 'DEBUG', 181 | 'class': 'logging.StreamHandler', 182 | 'formatter': 'verbose', 183 | }, 184 | }, 185 | 'loggers': { 186 | 'django.request': { 187 | 'handlers': ['mail_admins'], 188 | 'level': 'ERROR', 189 | 'propagate': True 190 | }, 191 | 'django.security.DisallowedHost': { 192 | 'level': 'ERROR', 193 | 'handlers': ['console', 'mail_admins'], 194 | 'propagate': True 195 | } 196 | } 197 | } 198 | 199 | 200 | # Your stuff... 201 | # ------------------------------------------------------------------------------ 202 | -------------------------------------------------------------------------------- /config/settings/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | Base settings to build other settings files upon. 3 | """ 4 | 5 | import environ 6 | 7 | ROOT_DIR = environ.Path(__file__) - 3 # (demoapp/config/settings/base.py - 3 = demoapp/) 8 | APPS_DIR = ROOT_DIR.path('demoapp') 9 | 10 | env = environ.Env() 11 | 12 | READ_DOT_ENV_FILE = env.bool('DJANGO_READ_DOT_ENV_FILE', default=False) 13 | if READ_DOT_ENV_FILE: 14 | # OS environment variables take precedence over variables from .env 15 | env.read_env(str(ROOT_DIR.path('.env'))) 16 | 17 | # GENERAL 18 | # ------------------------------------------------------------------------------ 19 | # https://docs.djangoproject.com/en/dev/ref/settings/#debug 20 | DEBUG = env.bool('DJANGO_DEBUG', False) 21 | # Local time zone. Choices are 22 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 23 | # though not all of them may be available with every OS. 24 | # In Windows, this must be set to your system time zone. 25 | TIME_ZONE = 'UTC' 26 | # https://docs.djangoproject.com/en/dev/ref/settings/#language-code 27 | LANGUAGE_CODE = 'en-us' 28 | # https://docs.djangoproject.com/en/dev/ref/settings/#site-id 29 | SITE_ID = 1 30 | # https://docs.djangoproject.com/en/dev/ref/settings/#use-i18n 31 | USE_I18N = True 32 | # https://docs.djangoproject.com/en/dev/ref/settings/#use-l10n 33 | USE_L10N = True 34 | # https://docs.djangoproject.com/en/dev/ref/settings/#use-tz 35 | USE_TZ = True 36 | 37 | # DATABASES 38 | # ------------------------------------------------------------------------------ 39 | # https://docs.djangoproject.com/en/dev/ref/settings/#databases 40 | DATABASES = { 41 | 'default': env.db('DATABASE_URL'), 42 | } 43 | DATABASES['default']['ATOMIC_REQUESTS'] = True 44 | 45 | # URLS 46 | # ------------------------------------------------------------------------------ 47 | # https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf 48 | ROOT_URLCONF = 'config.urls' 49 | # https://docs.djangoproject.com/en/dev/ref/settings/#wsgi-application 50 | WSGI_APPLICATION = 'config.wsgi.application' 51 | 52 | # APPS 53 | # ------------------------------------------------------------------------------ 54 | DJANGO_APPS = [ 55 | 'django.contrib.auth', 56 | 'django.contrib.contenttypes', 57 | 'django.contrib.sessions', 58 | 'django.contrib.sites', 59 | 'django.contrib.messages', 60 | 'django.contrib.staticfiles', 61 | # 'django.contrib.humanize', # Handy template tags 62 | 'django.contrib.admin', 63 | ] 64 | THIRD_PARTY_APPS = [ 65 | 'crispy_forms', 66 | 'allauth', 67 | 'allauth.account', 68 | 'allauth.socialaccount', 69 | 'rest_framework', 70 | 'django_keycloak.apps.KeycloakAppConfig', 71 | ] 72 | LOCAL_APPS = [ 73 | 'demoapp.users.apps.UsersAppConfig', 74 | # Your stuff: custom apps go here 75 | ] 76 | # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps 77 | INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS 78 | 79 | # MIGRATIONS 80 | # ------------------------------------------------------------------------------ 81 | # https://docs.djangoproject.com/en/dev/ref/settings/#migration-modules 82 | MIGRATION_MODULES = { 83 | 'sites': 'demoapp.contrib.sites.migrations' 84 | } 85 | 86 | # AUTHENTICATION 87 | # ------------------------------------------------------------------------------ 88 | # https://docs.djangoproject.com/en/dev/ref/settings/#authentication-backends 89 | AUTHENTICATION_BACKENDS = [ 90 | 'django.contrib.auth.backends.ModelBackend', 91 | 'django_keycloak.auth.backends.KeycloakAuthorizationCodeBackend', 92 | ] 93 | # 'allauth.account.auth_backends.AuthenticationBackend', 94 | 95 | # https://docs.djangoproject.com/en/dev/ref/settings/#auth-user-model 96 | AUTH_USER_MODEL = 'users.User' 97 | # https://docs.djangoproject.com/en/dev/ref/settings/#login-redirect-url 98 | LOGIN_REDIRECT_URL = 'users:redirect' 99 | # https://docs.djangoproject.com/en/dev/ref/settings/#login-url 100 | LOGIN_URL = 'http://192.168.1.47:6060/auth' 101 | 102 | # PASSWORDS 103 | # ------------------------------------------------------------------------------ 104 | # https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers 105 | PASSWORD_HASHERS = [ 106 | # https://docs.djangoproject.com/en/dev/topics/auth/passwords/#using-argon2-with-django 107 | 'django.contrib.auth.hashers.Argon2PasswordHasher', 108 | 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 109 | 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 110 | 'django.contrib.auth.hashers.BCryptSHA256PasswordHasher', 111 | 'django.contrib.auth.hashers.BCryptPasswordHasher', 112 | ] 113 | # https://docs.djangoproject.com/en/dev/ref/settings/#auth-password-validators 114 | AUTH_PASSWORD_VALIDATORS = [ 115 | { 116 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 117 | }, 118 | { 119 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 120 | }, 121 | { 122 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 123 | }, 124 | { 125 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 126 | }, 127 | ] 128 | 129 | # MIDDLEWARE 130 | # ------------------------------------------------------------------------------ 131 | # https://docs.djangoproject.com/en/dev/ref/settings/#middleware 132 | MIDDLEWARE = [ 133 | 'django.middleware.security.SecurityMiddleware', 134 | 'django.contrib.sessions.middleware.SessionMiddleware', 135 | 'django.middleware.common.CommonMiddleware', 136 | 'django.middleware.csrf.CsrfViewMiddleware', 137 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 138 | 'django.contrib.messages.middleware.MessageMiddleware', 139 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 140 | 'django_keycloak.middleware.BaseKeycloakMiddleware', 141 | ] 142 | 143 | # STATIC 144 | # ------------------------------------------------------------------------------ 145 | # https://docs.djangoproject.com/en/dev/ref/settings/#static-root 146 | STATIC_ROOT = str(ROOT_DIR('staticfiles')) 147 | # https://docs.djangoproject.com/en/dev/ref/settings/#static-url 148 | STATIC_URL = '/static/' 149 | # https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS 150 | STATICFILES_DIRS = [ 151 | str(APPS_DIR.path('static')), 152 | ] 153 | # https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#staticfiles-finders 154 | STATICFILES_FINDERS = [ 155 | 'django.contrib.staticfiles.finders.FileSystemFinder', 156 | 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 157 | ] 158 | 159 | # MEDIA 160 | # ------------------------------------------------------------------------------ 161 | # https://docs.djangoproject.com/en/dev/ref/settings/#media-root 162 | MEDIA_ROOT = str(APPS_DIR('media')) 163 | # https://docs.djangoproject.com/en/dev/ref/settings/#media-url 164 | MEDIA_URL = '/media/' 165 | 166 | # TEMPLATES 167 | # ------------------------------------------------------------------------------ 168 | # https://docs.djangoproject.com/en/dev/ref/settings/#templates 169 | TEMPLATES = [ 170 | { 171 | # https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-TEMPLATES-BACKEND 172 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 173 | # https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs 174 | 'DIRS': [ 175 | str(APPS_DIR.path('templates')), 176 | ], 177 | 'OPTIONS': { 178 | # https://docs.djangoproject.com/en/dev/ref/settings/#template-debug 179 | 'debug': DEBUG, 180 | # https://docs.djangoproject.com/en/dev/ref/settings/#template-loaders 181 | # https://docs.djangoproject.com/en/dev/ref/templates/api/#loader-types 182 | 'loaders': [ 183 | 'django.template.loaders.filesystem.Loader', 184 | 'django.template.loaders.app_directories.Loader', 185 | ], 186 | # https://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors 187 | 'context_processors': [ 188 | 'django.template.context_processors.debug', 189 | 'django.template.context_processors.request', 190 | 'django.contrib.auth.context_processors.auth', 191 | 'django.template.context_processors.i18n', 192 | 'django.template.context_processors.media', 193 | 'django.template.context_processors.static', 194 | 'django.template.context_processors.tz', 195 | 'django.contrib.messages.context_processors.messages', 196 | ], 197 | }, 198 | }, 199 | ] 200 | # http://django-crispy-forms.readthedocs.io/en/latest/install.html#template-packs 201 | CRISPY_TEMPLATE_PACK = 'bootstrap4' 202 | 203 | # FIXTURES 204 | # ------------------------------------------------------------------------------ 205 | # https://docs.djangoproject.com/en/dev/ref/settings/#fixture-dirs 206 | FIXTURE_DIRS = ( 207 | str(APPS_DIR.path('fixtures')), 208 | ) 209 | 210 | # EMAIL 211 | # ------------------------------------------------------------------------------ 212 | # https://docs.djangoproject.com/en/dev/ref/settings/#email-backend 213 | EMAIL_BACKEND = env('DJANGO_EMAIL_BACKEND', default='django.core.mail.backends.smtp.EmailBackend') 214 | 215 | # ADMIN 216 | # ------------------------------------------------------------------------------ 217 | # Django Admin URL. 218 | ADMIN_URL = 'admin/' 219 | # https://docs.djangoproject.com/en/dev/ref/settings/#admins 220 | ADMINS = [ 221 | ("""Sairam Krish""", 'haisairam@gmail.com'), 222 | ] 223 | # https://docs.djangoproject.com/en/dev/ref/settings/#managers 224 | MANAGERS = ADMINS 225 | 226 | 227 | # django-allauth 228 | # ------------------------------------------------------------------------------ 229 | ACCOUNT_ALLOW_REGISTRATION = env.bool('DJANGO_ACCOUNT_ALLOW_REGISTRATION', True) 230 | # https://django-allauth.readthedocs.io/en/latest/configuration.html 231 | ACCOUNT_AUTHENTICATION_METHOD = 'username' 232 | # https://django-allauth.readthedocs.io/en/latest/configuration.html 233 | ACCOUNT_EMAIL_REQUIRED = True 234 | # https://django-allauth.readthedocs.io/en/latest/configuration.html 235 | ACCOUNT_EMAIL_VERIFICATION = 'mandatory' 236 | # https://django-allauth.readthedocs.io/en/latest/configuration.html 237 | ACCOUNT_ADAPTER = 'demoapp.users.adapters.AccountAdapter' 238 | # https://django-allauth.readthedocs.io/en/latest/configuration.html 239 | SOCIALACCOUNT_ADAPTER = 'demoapp.users.adapters.SocialAccountAdapter' 240 | 241 | 242 | # Your stuff... 243 | # ------------------------------------------------------------------------------ 244 | --------------------------------------------------------------------------------