├── app ├── client │ └── app.js ├── common │ ├── __init__.py │ ├── analytics.py │ ├── utils │ │ ├── __init__.py │ │ └── models.py │ ├── fields.py │ └── enums.py ├── core │ ├── __init__.py │ ├── celery_beat.py │ ├── api_urls.py │ ├── urls.py │ ├── wsgi.py │ ├── celery_app.py │ ├── gunicorn.conf.py │ └── settings.py ├── templates │ ├── 404.html │ ├── 500.html │ └── base.html ├── first_party_app │ ├── admin.py │ ├── __init__.py │ ├── api_urls.py │ ├── api_views.py │ ├── serializers.py │ ├── tests │ │ ├── __init__.py │ │ └── test_models.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0002_user_political_party.py │ │ └── 0001_initial.py │ ├── tasks.py │ └── models.py ├── node_modules │ └── .keep-folder-in-repo-leave-empty ├── ops │ ├── dev_shell.sh │ ├── beat_health.sh │ ├── web_health.sh │ ├── worker_health.sh │ ├── web_launch.sh │ ├── worker_launch.sh │ ├── beat_launch.sh │ └── dev │ │ └── clean.sh ├── setup.cfg ├── manage.py ├── package.json ├── Pipfile ├── webpack.config.js ├── echo.py └── Pipfile.lock ├── README.md ├── scripts ├── local_build.sh ├── cmd_run.sh ├── up.sh └── docker_build_step2.sh ├── Dockerfile-dev-echo ├── .travis.yml ├── Dockerfile-dev ├── Makefile ├── Dockerfile ├── setup ├── LICENSE ├── .gitignore └── docker-compose.yaml /app/client/app.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/common/analytics.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/templates/404.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/templates/500.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/templates/base.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # django-starter -------------------------------------------------------------------------------- /app/common/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/first_party_app/admin.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/first_party_app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/first_party_app/api_urls.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/first_party_app/api_views.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/first_party_app/serializers.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/first_party_app/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/first_party_app/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/node_modules/.keep-folder-in-repo-leave-empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/ops/dev_shell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | /bin/bash 3 | pipenv shell 4 | -------------------------------------------------------------------------------- /scripts/local_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | docker-compose build 4 | -------------------------------------------------------------------------------- /scripts/cmd_run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | docker-compose exec server pipenv run $1 4 | -------------------------------------------------------------------------------- /app/ops/beat_health.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ -e "/app/celerybeat-checkable.pid" ] || exit 1 4 | -------------------------------------------------------------------------------- /app/ops/web_health.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | curl --fail http://localhost:8000/-/health/ || exit 1 4 | -------------------------------------------------------------------------------- /Dockerfile-dev-echo: -------------------------------------------------------------------------------- 1 | FROM python:3.9-buster 2 | 3 | WORKDIR /app 4 | 5 | RUN pip install netifaces==0.10.9 6 | -------------------------------------------------------------------------------- /app/core/celery_beat.py: -------------------------------------------------------------------------------- 1 | from .celery_app import app 2 | 3 | print("Beat Schedule") 4 | print(app.conf.beat_schedule) 5 | -------------------------------------------------------------------------------- /app/ops/worker_health.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pipenv run celery inspect ping -A core.celery_app -d celery@$HOSTNAME 4 | -------------------------------------------------------------------------------- /app/ops/web_launch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pipenv run gunicorn -b 0.0.0.0:8000 -c /app/core/gunicorn.conf.py core.wsgi_prod 4 | -------------------------------------------------------------------------------- /scripts/up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | trap "docker-compose stop" EXIT 4 | 5 | docker-compose up --build -d 6 | docker-compose logs -f -t --tail=100 7 | -------------------------------------------------------------------------------- /app/ops/worker_launch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pipenv run celery -A turnout.celery_app worker -Q ${1} --without-heartbeat --without-mingle --without-gossip 4 | -------------------------------------------------------------------------------- /app/ops/beat_launch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | pipenv run celery -A core.celery_beat beat --scheduler redbeat.RedBeatScheduler --pidfile="/app/celerybeat-checkable.pid" 4 | -------------------------------------------------------------------------------- /app/core/api_urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import include, path 2 | 3 | app_name: str = "api" 4 | urlpatterns = [ 5 | path("first_party/", include("first_party_app.api_urls")), 6 | ] 7 | -------------------------------------------------------------------------------- /app/first_party_app/tasks.py: -------------------------------------------------------------------------------- 1 | from celery import shared_task 2 | from django.core.cache import cache 3 | 4 | from .models import User 5 | 6 | 7 | @shared_task 8 | def cache_user_count(): 9 | cache.set("total_users", User.objects.count()) 10 | -------------------------------------------------------------------------------- /app/core/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import include, path 3 | 4 | urlpatterns = [ 5 | path("-/", include("django_alive.urls")), 6 | path("admin/", admin.site.urls), 7 | # path("v1/", include("core.api_urls")), 8 | ] 9 | -------------------------------------------------------------------------------- /app/ops/dev/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | autoflake --remove-unused-variables --remove-all-unused-imports --ignore-init-module-imports --in-place --recursive --exclude /*/migrations/* /app/ 4 | 5 | isort -m 3 -w 88 --skip migrations /app/ 6 | 7 | black --exclude /*/migrations/* /app/ 8 | -------------------------------------------------------------------------------- /scripts/docker_build_step2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npm run build 4 | 5 | export SECRET_KEY=abcd 6 | pipenv run /app/manage.py collectstatic --noinput 7 | 8 | # Save space by deleting unnecessary content 9 | rm -rf /root/.cache 10 | rm -rf /app/node_modules/ 11 | rm -rf /app/assets/ 12 | -------------------------------------------------------------------------------- /app/first_party_app/tests/test_models.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from model_bakery import baker 3 | 4 | from ..models import User 5 | 6 | 7 | @pytest.mark.django_db 8 | def test_user(): 9 | user = baker.make("first_party_app.User", email="great@cool.com") 10 | assert str(user) == "great@cool.com" 11 | assert User.objects.count() == 1 12 | -------------------------------------------------------------------------------- /app/common/fields.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING, Type 2 | 3 | from django.db import models 4 | from enumfields.fields import EnumFieldMixin 5 | 6 | if TYPE_CHECKING: 7 | from enumfields import Enum 8 | 9 | 10 | class EnumField(EnumFieldMixin, models.TextField): 11 | def __init__(self, enum: Type["Enum"], **options) -> None: 12 | super().__init__(enum, **options) # type: ignore 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: bionic 2 | language: python 3 | python: 4 | - "3.8" 5 | 6 | services: 7 | - docker 8 | 9 | before_install: 10 | - touch .env 11 | 12 | install: 13 | - docker-compose build 14 | 15 | before_script: 16 | - docker-compose up -d 17 | 18 | script: 19 | - docker-compose exec server pipenv run pytest -n 2 /app/ 20 | - docker-compose exec server pipenv run mypy /app/ 21 | 22 | notifications: 23 | email: false 24 | -------------------------------------------------------------------------------- /app/common/enums.py: -------------------------------------------------------------------------------- 1 | from enum import EnumMeta 2 | 3 | from enumfields import Enum 4 | 5 | 6 | class PoliticalParty(Enum, metaclass=EnumMeta): 7 | NONE = "None" 8 | DEMOCRATIC = "Dem" 9 | REPUBLICAN = "GOP" 10 | GREEN = "Green" 11 | LIBERTARIAN = "Lib" 12 | OTHER = "Other" 13 | 14 | class Labels: 15 | DEMOCRATIC = "Democrat" 16 | REPUBLICAN = "Republican" 17 | LIBERTARIAN = "Libertarian" 18 | -------------------------------------------------------------------------------- /app/core/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for app project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "app.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /app/common/utils/models.py: -------------------------------------------------------------------------------- 1 | import uuid 2 | 3 | from django.db import models 4 | 5 | 6 | class TimestampModel(models.Model): 7 | created_at = models.DateTimeField(auto_now_add=True) 8 | modified_at = models.DateTimeField(auto_now=True) 9 | 10 | class Meta: 11 | abstract = True 12 | 13 | 14 | class UUIDModel(models.Model): 15 | uuid = models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True) 16 | 17 | class Meta: 18 | abstract = True 19 | -------------------------------------------------------------------------------- /Dockerfile-dev: -------------------------------------------------------------------------------- 1 | FROM python:3.9-buster 2 | 3 | RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - && \ 4 | apt-get update && \ 5 | apt-get install -y nodejs && \ 6 | pip install pipenv==2020.11.15 && \ 7 | apt-get clean 8 | 9 | ENV APP_DIR=/app 10 | WORKDIR $APP_DIR 11 | 12 | RUN apt-get update && apt-get install -y wait-for-it && apt-get clean 13 | 14 | COPY app/package.json app/package-lock.json $APP_DIR/ 15 | RUN npm install 16 | 17 | COPY app/Pipfile app/Pipfile.lock $APP_DIR/ 18 | RUN pipenv install --dev 19 | -------------------------------------------------------------------------------- /app/core/celery_app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | 4 | import celery 5 | import ddtrace 6 | 7 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") 8 | 9 | if os.environ.get("DATADOG_API_KEY"): 10 | ddtrace.patch_all() 11 | 12 | app = celery.Celery("uptime") 13 | 14 | app.config_from_object("django.conf:settings", namespace="CELERY") 15 | 16 | app.autodiscover_tasks() 17 | 18 | 19 | @app.task(bind=True) 20 | def debug_task(self): 21 | time.sleep(3) 22 | return {"uptime": "it's all looking up from here"} 23 | -------------------------------------------------------------------------------- /app/first_party_app/migrations/0002_user_political_party.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.6 on 2021-02-09 14:58 2 | 3 | import common.enums 4 | import common.fields 5 | from django.db import migrations 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('first_party_app', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='user', 17 | name='political_party', 18 | field=common.fields.EnumField(default='Dem', enum=common.enums.PoliticalParty), 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /app/setup.cfg: -------------------------------------------------------------------------------- 1 | [tool:pytest] 2 | DJANGO_SETTINGS_MODULE = core.settings 3 | filterwarnings = 4 | ignore::DeprecationWarning 5 | 6 | [mypy] 7 | plugins = 8 | mypy_django_plugin.main, 9 | mypy_drf_plugin.main 10 | ignore_missing_imports = True 11 | # Due to our use of null in models, we get a lot of strict optional errors. 12 | # This is ugly, but works 13 | no_strict_optional = True 14 | 15 | [mypy.plugins.django-stubs] 16 | django_settings_module = "core.settings" 17 | 18 | [mypy-*.migrations.*] 19 | ignore_errors = True 20 | 21 | [tool.isort] 22 | profile = "black" 23 | multi_line_output = 3 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Running too many tests in parallel exhausts shared memory on some machines 2 | MAXPROC ?= 16 3 | 4 | up: 5 | scripts/up.sh 6 | 7 | build: 8 | scripts/local_build.sh 9 | 10 | shell: 11 | docker-compose exec server pipenv run /bin/bash 12 | 13 | clean: 14 | docker-compose exec server pipenv run /app/ops/dev/clean.sh 15 | 16 | mypy: 17 | docker-compose exec server pipenv run mypy /app/ 18 | 19 | pytest: 20 | docker-compose exec server pipenv run pytest -n 2 /app/ 21 | 22 | migrate: 23 | docker-compose exec server pipenv run /app/manage.py migrate 24 | 25 | createsuperuser: 26 | docker-compose exec server pipenv run /app/manage.py createsuperuser 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9-buster 2 | 3 | RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - && \ 4 | apt-get update && \ 5 | apt-get install -y nodejs && \ 6 | pip install pipenv==2020.11.15 && \ 7 | apt-get clean 8 | 9 | ENV APP_DIR=/app 10 | WORKDIR $APP_DIR 11 | 12 | COPY app/package.json app/package-lock.json $APP_DIR/ 13 | RUN npm install 14 | 15 | COPY app/Pipfile app/Pipfile.lock $APP_DIR/ 16 | RUN pipenv install 17 | 18 | COPY scripts/docker_build_step2.sh /root/ 19 | COPY app/ $APP_DIR/ 20 | RUN bash /root/docker_build_step2.sh && rm /root/docker_build_step2.sh 21 | 22 | ARG TAG_ARG 23 | ARG BUILD_ARG 24 | ENV TAG=$TAG_ARG 25 | ENV BUILD=$BUILD_ARG 26 | 27 | EXPOSE 8000 28 | CMD ["/app/ops/web_launch.sh"] 29 | -------------------------------------------------------------------------------- /app/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == "__main__": 22 | main() 23 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "dependencies": {}, 7 | "devDependencies": { 8 | "autoprefixer": "^9.7.3", 9 | "css-loader": "^3.3.2", 10 | "exports-loader": "^0.7.0", 11 | "node-sass": "^4.14.1", 12 | "postcss-loader": "^3.0.0", 13 | "sass-loader": "^8.0.2", 14 | "style-loader": "^1.0.1", 15 | "webpack": "^4.44.1", 16 | "webpack-cli": "^3.3.12", 17 | "webpack-livereload-plugin": "^2.2.0" 18 | }, 19 | "scripts": { 20 | "develop": "npm install && node_modules/webpack-cli/bin/cli.js --watch --env production", 21 | "build": "node_modules/webpack-cli/bin/cli.js -p --env production" 22 | }, 23 | "keywords": [], 24 | "author": "", 25 | "license": "ISC" 26 | } 27 | -------------------------------------------------------------------------------- /setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # See https://sipb.mit.edu/doc/safe-shell/ 4 | set -euf -o pipefail 5 | 6 | if [ -t 1 ]; then 7 | export NORMAL=$(tput setaf 0) 8 | export RED=$(tput setaf 1) 9 | export GREEN=$(tput setaf 2) 10 | export YELLOW=$(tput setaf 3) 11 | export BLUE=$(tput setaf 4) 12 | export MAGENTA=$(tput setaf 5) 13 | export CYAN=$(tput setaf 6) 14 | export WHITE=$(tput setaf 7) 15 | export BOLD=$(tput bold) 16 | export REVERSE=$(tput rev) 17 | export RESET=$(tput sgr0) 18 | else 19 | export NORMAL="" 20 | export RED="" 21 | export GREEN="" 22 | export YELLOW="" 23 | export BLUE="" 24 | export MAGENTA="" 25 | export CYAN="" 26 | export WHITE="" 27 | export BOLD="" 28 | export REVERSE="" 29 | export RESET="" 30 | fi 31 | 32 | echo "${GREEN}${BOLD}Hi! I'm here to setup your new project${RESET}" 33 | echo "${CYAN}Creating a .env file${RESET}" 34 | touch .env 35 | echo "Environment File Created" 36 | -------------------------------------------------------------------------------- /app/core/gunicorn.conf.py: -------------------------------------------------------------------------------- 1 | import multiprocessing 2 | 3 | from psycogreen.gevent import patch_psycopg 4 | 5 | timeout: int = 75 6 | keepalive: int = 75 7 | accesslog: str = "-" 8 | errorlog: str = "-" 9 | num_proc: int = multiprocessing.cpu_count() 10 | worker_class: str = "gevent" 11 | workers: int = (num_proc * 2) + 1 12 | access_log_format: str = ( 13 | '{"message":"%(h)s %({x-forwarded-for}i)s %(l)s %(u)s %(t)s \'%(r)s\' %(s)s %(b)s %(f)s %(a)s",' 14 | '"remote_ip":"%(h)s","request_id":"%({X-Request-Id}i)s","response_code":"%(s)s",' 15 | '"request_method":"%(m)s","request_path":"%(U)s","request_querystring":"%(q)s",' 16 | '"request_timetaken":"%(D)s","response_length":"%(B)s","user_agent":"%(a)s",' 17 | '"cf_ipaddress":"%({CF-Connecting-IP}i)s","cf_country":"%({CF-IPCountry}i)s",' 18 | '"cf_ray":"%({CF-Ray}i)s","forwarded_for":"%({x-forwarded-for}i)s"}' 19 | ) 20 | 21 | 22 | def post_fork(server, worker): 23 | patch_psycopg() 24 | -------------------------------------------------------------------------------- /app/first_party_app/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import models as auth_models 2 | from django.db import models 3 | from django.utils.translation import gettext_lazy as _ 4 | 5 | from common.enums import PoliticalParty 6 | from common.fields import EnumField 7 | from common.utils.models import TimestampModel, UUIDModel 8 | 9 | 10 | class User( 11 | UUIDModel, 12 | TimestampModel, 13 | auth_models.AbstractBaseUser, 14 | auth_models.PermissionsMixin, 15 | ): 16 | is_staff = models.BooleanField(_("Staff Status"), default=False) 17 | is_active = models.BooleanField(_("Active"), default=True) 18 | 19 | email = models.EmailField(_("Email Address"), editable=False, unique=True) 20 | 21 | political_party = EnumField(PoliticalParty, default=PoliticalParty.DEMOCRATIC) 22 | 23 | USERNAME_FIELD = "email" 24 | 25 | class Meta: 26 | verbose_name = _("User") 27 | verbose_name_plural = _("Users") 28 | ordering = ["created_at"] 29 | 30 | def __str__(self): 31 | return self.email 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nick Catalano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | psycopg2 = "==2.8.*" 8 | boto3 = "==1.17.*" 9 | celery = {version = "==5.0.*", extras = ["redis"]} 10 | celery-redbeat = "==2.0.*" 11 | django-celery-results = "==2.0.*" 12 | djangorestframework = "==3.12.*" 13 | environs = "==9.0.*" 14 | requests = "==2.22.*" 15 | watchdog = "==1.0.*" 16 | python-json-logger = "==2.0.*" 17 | sentry-sdk = "==0.19.*" 18 | redis = "==3.5.*" 19 | django-redis = "==4.12.*" 20 | gunicorn = {version = "==20.0.*", extras = ["gevent"]} 21 | psycogreen = "==1.0.*" 22 | django-db-geventpool = "==3.2.2" 23 | ddtrace = "==0.45.*" 24 | datadog = "==0.39.*" 25 | django-alive = "==1.1.*" 26 | whitenoise = "==5.0.*" 27 | Django = "==3.1.*" 28 | django-enumfields = "==2.0.*" 29 | dj-database-url = "==0.5.*" 30 | 31 | [dev-packages] 32 | ipython = "==7.10.*" 33 | pytest-django = "==4.1.*" 34 | pytest-xdist = "==2.2.*" 35 | django-stubs = "==1.7.*" 36 | djangorestframework-stubs = "==1.4.*" 37 | autoflake = "==1.4.*" 38 | black = "==20.8b1" 39 | isort = "==5.7.*" 40 | PyYAML = "==5.4.*" 41 | argh = "==0.26.*" 42 | model-bakery = "==1.2.*" 43 | 44 | [requires] 45 | python_version = "3.9" 46 | -------------------------------------------------------------------------------- /app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const LiveReloadPlugin = require("webpack-livereload-plugin"); 3 | 4 | module.exports = { 5 | entry: "./client/app.js", 6 | output: { 7 | filename: "bundle.js", 8 | path: path.resolve(__dirname, "dist"), 9 | publicPath: "/static", 10 | }, 11 | plugins: [ 12 | new LiveReloadPlugin({ 13 | port: 35729, 14 | hostname: "localhost", 15 | }), 16 | ], 17 | mode: "development", 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.(scss)$/, 22 | use: [ 23 | { 24 | // Adds CSS to the DOM by injecting a `