├── .babelrc ├── .buildpacks ├── .coveragerc ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmrc ├── .travis.yml ├── Makefile ├── Procfile ├── README.rst ├── Vagrantfile ├── app.json ├── circle.yml ├── docs ├── Makefile ├── conf.py ├── dev │ ├── index.rst │ ├── provisioning.rst │ ├── settings.rst │ └── translation.rst ├── index.rst └── userguide │ └── index.rst ├── dokku_first_deploy.sh ├── gulpfile.js ├── locale └── .gitkeep ├── manage.py ├── nginx.conf.sigil ├── package.json ├── project.travis.yml ├── project_name ├── __init__.py ├── load_env.py ├── settings │ ├── __init__.py │ ├── base.py │ ├── deploy.py │ ├── dev.py │ └── local.example.py ├── static │ ├── 502.html │ ├── js │ │ ├── csrf_ajax.js │ │ └── index.js │ ├── less │ │ └── index.less │ └── robots.txt ├── templates │ ├── 404.html │ ├── 500.html │ └── base.html ├── urls.py └── wsgi.py ├── requirements.txt ├── requirements ├── base.txt ├── dev.txt └── production.txt ├── runtime.txt ├── scripts ├── postdeploy.sh └── predeploy.sh └── setup.cfg /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ], 5 | "plugins": [ 6 | "transform-react-jsx" 7 | ], 8 | "env": { 9 | "test": { 10 | "plugins": [ 11 | "rewire" 12 | ] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.buildpacks: -------------------------------------------------------------------------------- 1 | https://github.com/heroku/heroku-buildpack-nodejs.git 2 | https://github.com/heroku/heroku-buildpack-python.git 3 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = true 3 | omit = */tests/*, */migrations/*, */urls.py, */settings/*, */wsgi.py, manage.py, fabfile.py 4 | source = . 5 | 6 | [report] 7 | show_missing = true 8 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | bundle.js 2 | vendors.js 3 | csrf_ajax.js 4 | polyfill-orientationchange.js 5 | modernizr.js 6 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | // I want to use babel-eslint for parsing! 3 | "parser": "babel-eslint", 4 | 5 | "env": { 6 | // I write for browser 7 | "browser": true, 8 | // in CommonJS 9 | "node": true 10 | }, 11 | 12 | // To give you an idea how to override rule options: 13 | "rules": { 14 | "comma-dangle": ["error", "only-multiline"], 15 | "eqeqeq": "error", 16 | "eol-last": [0], 17 | "no-mixed-requires": [0], 18 | "no-underscore-dangle": [0], 19 | "prefer-template": ["warn"], 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw[po] 2 | *.py[co] 3 | .project 4 | .pydevproject 5 | *~ 6 | *.db 7 | *.orig 8 | *.DS_Store 9 | .coverage 10 | coverage 11 | .vagrant 12 | */settings/local.py 13 | public/static/ 14 | public/media/ 15 | conf/pillar/*/deploy 16 | *.priv 17 | .env 18 | node_modules 19 | */static/js/bundle.js 20 | */static/js/vendors.js 21 | */static/libs/modernizr.js 22 | */static/css 23 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | python=python3.7 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # 2 | # IMPORTANT NOTE: This .travis.yml file is intended for 3 | # testing the project template itself. If you want to 4 | # test a project created from the project template, 5 | # please rename "project.travis.yml" to ".travis.yml", 6 | # overwriting this file. "make setup" will do this 7 | # for you. 8 | 9 | language: python 10 | 11 | sudo: false 12 | 13 | python: 14 | - "3.7" 15 | 16 | # Match postgresql version to that in conf/pillar/project.sls 17 | services: 18 | - postgresql 19 | 20 | # In general, cache things where they are. 21 | directories: 22 | - $HOME/.cache/pip 23 | - $HOME/.npm 24 | - $HOME/.nvm 25 | - $HOME/.cache/node_modules 26 | - $HOME/.cache/venvs 27 | 28 | env: 29 | - WORKON_HOME=$HOME/.cache/venvs 30 | 31 | install: 32 | - pip install -U pip wheel 33 | - pip install -U 'Django>2.0' 34 | - mkdir -p $WORKON_HOME 35 | - nvm install 10.16.0 36 | - nvm use 10.16.0 37 | 38 | script: 39 | - django-admin.py startproject --template=https://github.com/caktus/django-project-template/zipball/${TRAVIS_BRANCH} --extension=py,yml --name=Makefile,gulpfile.js,package.json test_template 40 | - cd test_template 41 | - make setup 42 | - make lint 43 | - $WORKON_HOME/test_template/bin/python manage.py test --noinput 44 | - . $WORKON_HOME/test_template/bin/activate 45 | - make docs 46 | 47 | before_cache: 48 | - rm -f $HOME/.cache/pip/log/debug.log 49 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PROJECT_NAME = {{ project_name }} 2 | STATIC_DIR = ./$(PROJECT_NAME)/static 3 | 4 | default: lint test 5 | 6 | test: 7 | # Run all tests and report coverage 8 | # Requires coverage 9 | python manage.py makemigrations --dry-run | grep 'No changes detected' || \ 10 | (echo 'There are changes which require migrations.' && exit 1) 11 | coverage run manage.py test 12 | coverage report -m --fail-under 80 13 | npm test 14 | 15 | lint-py: 16 | # Check for Python formatting issues 17 | # Requires flake8 18 | $(WORKON_HOME)/{{ project_name }}/bin/flake8 . 19 | 20 | lint-js: 21 | # Check JS for any problems 22 | # Requires jshint 23 | ./node_modules/.bin/eslint -c .eslintrc '${STATIC_DIR}' --ext js,jsx 24 | 25 | lint: lint-py lint-js 26 | 27 | # Generate a random string of desired length 28 | generate-secret: length = 32 29 | generate-secret: 30 | @strings /dev/urandom | grep -o '[[:alnum:]]' | head -n $(length) | tr -d '\n'; echo 31 | 32 | conf/keys/%.pub.ssh: 33 | # Generate SSH deploy key for a given environment 34 | ssh-keygen -t rsa -b 4096 -f $*.priv -C "$*@${PROJECT_NAME}" 35 | @mv $*.priv.pub $@ 36 | 37 | staging-deploy-key: conf/keys/staging.pub.ssh 38 | 39 | production-deploy-key: conf/keys/production.pub.ssh 40 | 41 | # Translation helpers 42 | makemessages: 43 | # Extract English messages from our source code 44 | python manage.py makemessages --ignore 'conf/*' --ignore 'docs/*' --ignore 'requirements/*' \ 45 | --no-location --no-obsolete -l en 46 | 47 | compilemessages: 48 | # Compile PO files into the MO files that Django will use at runtime 49 | python manage.py compilemessages 50 | 51 | pushmessages: 52 | # Upload the latest English PO file to Transifex 53 | tx push -s 54 | 55 | pullmessages: 56 | # Pull the latest translated PO files from Transifex 57 | tx pull -af 58 | 59 | setup: 60 | virtualenv -p `which python3.7` $(WORKON_HOME)/{{ project_name }} 61 | $(WORKON_HOME)/{{ project_name }}/bin/pip install -U pip wheel 62 | $(WORKON_HOME)/{{ project_name }}/bin/pip install -Ur requirements/dev.txt 63 | $(WORKON_HOME)/{{ project_name }}/bin/pip freeze 64 | npm install 65 | npm update 66 | cp {{ project_name }}/settings/local.example.py {{ project_name }}/settings/local.py 67 | echo "DJANGO_SETTINGS_MODULE={{ project_name }}.settings.local" > .env 68 | createdb -E UTF-8 {{ project_name }} 69 | $(WORKON_HOME)/{{ project_name }}/bin/python manage.py migrate 70 | if [ -e project.travis.yml ] ; then mv project.travis.yml .travis.yml; fi 71 | @echo 72 | @echo "The {{ project_name }} project is now setup on your machine." 73 | @echo "Run the following commands to activate the virtual environment and run the" 74 | @echo "development server:" 75 | @echo 76 | @echo " workon {{ project_name }}" 77 | @echo " npm run dev" 78 | 79 | update: 80 | $(WORKON_HOME)/{{ project_name }}/bin/pip install -U -r requirements/dev.txt 81 | npm install 82 | npm update 83 | 84 | # Build documentation 85 | docs: 86 | cd docs && make html 87 | 88 | .PHONY: default test lint lint-py lint-js generate-secret makemessages \ 89 | pushmessages pullmessages compilemessages docs 90 | 91 | .PRECIOUS: conf/keys/%.pub.ssh 92 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn --timeout 300 {{ project_name }}.wsgi 2 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | {% if False %} 2 | 3 | .. image:: https://requires.io/github/caktus/django-project-template/requirements.svg?branch=master 4 | 5 | (See `our requires.io documentation `_). 6 | 7 | Installation 8 | ============ 9 | 10 | To start a new project with this template:: 11 | 12 | django-admin.py startproject \ 13 | --template=https://github.com/caktus/django-project-template/zipball/master \ 14 | --extension=py,rst,yml,sh,js \ 15 | --name=Makefile,gulpfile.js,package.json,Procfile \ 16 | 17 | 18 | License 19 | ======= 20 | 21 | Copyright (c) 2017, Caktus Consulting Group, LLC 22 | 23 | Redistribution and use in source and binary forms, with or without modification, are permitted 24 | provided that the following conditions are met: 25 | 26 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions 27 | and the following disclaimer. 28 | 29 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 30 | and the following disclaimer in the documentation and/or other materials provided with the 31 | distribution. 32 | 33 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse 34 | or promote products derived from this software without specific prior written permission. 35 | 36 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 37 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 38 | FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 39 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 40 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 41 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 42 | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 43 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 44 | 45 | {% endif %} 46 | 47 | .. EDIT the below links to use the project's github repo path. Or just remove them. 48 | 49 | .. image:: https://requires.io/github/GITHUB_ORG/{{ project_name }}/requirements.svg?branch=master 50 | .. image:: https://requires.io/github/GITHUB_ORG/{{ project_name }}/requirements.svg?branch=develop 51 | 52 | {{ project_name|title }} 53 | ======================== 54 | 55 | Below you will find basic setup and deployment instructions for the {{ project_name }} 56 | project. To begin you should have the following applications installed on your 57 | local development system: 58 | 59 | - Python >= 3.7 60 | - NodeJS >= 10.16 61 | - `pip `_ >= 19 62 | - `virtualenv `_ >= 1.10 63 | - `virtualenvwrapper `_ >= 3.0 64 | - Postgres >= 9.3 65 | - git >= 1.7 66 | 67 | Installing the proper NodeJS versions for each of your projects can be difficult. It's probably best 68 | to `use nvm `_. 69 | 70 | Django version 71 | ------------------------ 72 | 73 | The Django version configured in this template is conservative. If you want to 74 | use a newer version, edit ``requirements/base.txt``. 75 | 76 | Getting Started 77 | ------------------------ 78 | 79 | First clone the repository from Github and switch to the new directory:: 80 | 81 | $ git clone git@github.com:[ORGANIZATION]/{{ project_name }}.git 82 | $ cd {{ project_name }} 83 | 84 | To setup your local environment you can use the quickstart make target `setup`, which will 85 | install both Python and Javascript dependencies (via pip and npm) into a virtualenv named 86 | "{{ project_name }}", configure a local django settings file, and create a database via 87 | Postgres named "{{ project_name }}" with all migrations run:: 88 | 89 | $ make setup 90 | $ workon {{ project_name }} 91 | 92 | If you require a non-standard setup, you can walk through the manual setup steps below making 93 | adjustments as necessary to your needs. 94 | 95 | To setup your local environment you should create a virtualenv and install the 96 | necessary requirements:: 97 | 98 | # Check that you have python3.7 installed 99 | $ which python3.7 100 | $ mkvirtualenv {{ project_name }} -p `which python3.7` 101 | ({{ project_name }})$ pip install -r requirements/dev.txt 102 | ({{ project_name }})$ npm install 103 | 104 | Next, we'll set up our local environment variables. We use `django-dotenv 105 | `_ to help with this. It reads environment variables 106 | located in a file name ``.env`` in the top level directory of the project. The only variable we need 107 | to start is ``DJANGO_SETTINGS_MODULE``:: 108 | 109 | ({{ project_name }})$ cp {{ project_name }}/settings/local.example.py {{ project_name }}/settings/local.py 110 | ({{ project_name }})$ echo "DJANGO_SETTINGS_MODULE={{ project_name }}.settings.local" > .env 111 | 112 | Create the Postgres database and run the initial migrate:: 113 | 114 | ({{ project_name }})$ createdb -E UTF-8 {{ project_name }} 115 | ({{ project_name }})$ python manage.py migrate 116 | 117 | If you want to use `Travis `_ to test your project, 118 | rename ``project.travis.yml`` to ``.travis.yml``, overwriting the ``.travis.yml`` 119 | that currently exists. (That one is for testing the template itself.):: 120 | 121 | ({{ project_name }})$ mv project.travis.yml .travis.yml 122 | 123 | Development 124 | ----------- 125 | 126 | You should be able to run the development server via the configured `dev` script:: 127 | 128 | ({{ project_name }})$ npm run dev 129 | 130 | Or, on a custom port and address:: 131 | 132 | ({{ project_name }})$ npm run dev -- --address=0.0.0.0 --port=8020 133 | 134 | Any changes made to Python, Javascript or Less files will be detected and rebuilt transparently as 135 | long as the development server is running. 136 | 137 | Deployment 138 | ---------- 139 | 140 | There are different ways to deploy, and `this document `_ outlines a few of them that could be used for {{ project_name }}. 141 | 142 | Deployment with fabric 143 | ...................... 144 | 145 | We use a library called `fabric `_ as a wrapper around a lot of our deployment 146 | functionality. However, deployment is no longer fully set up in this template, and instead you'll need 147 | to do something like set up `Tequila `_ for your project. Currently, 148 | best way to do that is to copy the configuration from an existing project. Once that is done, and the 149 | servers have been provisioned, you can deploy changes to a particular environment with the ``deploy`` 150 | command:: 151 | 152 | $ fab staging deploy 153 | 154 | Deployment with Dokku 155 | ..................... 156 | 157 | Alternatively, you can deploy the project using Dokku. See the 158 | `Caktus developer docs `_. 159 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.require_version ">= 1.8.7" 2 | 3 | Vagrant.configure("2") do |config| 4 | config.vm.box = "kaorimatz/ubuntu-16.04-amd64" 5 | config.vm.network :private_network, ip: "33.33.33.10" 6 | config.vm.provider :virtualbox do |vbox| 7 | vbox.customize ["modifyvm", :id, "--memory", "1024"] 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "Heroku supports a lot of other stuff here but dokku only supports scripts.dokku", 3 | "scripts": { 4 | "dokku": { 5 | "predeploy": "bash scripts/predeploy.sh", 6 | "postdeploy": "bash scripts/postdeploy.sh" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | python: 3 | version: 3.7 4 | environment: 5 | DJANGO_SETTINGS_MODULE: {{ project_name }}.settings.dev 6 | dependencies: 7 | override: 8 | - pip install -U pip 9 | - pip install -r requirements/dev.txt 10 | - nvm install 4.2 11 | - nvm use 4.2 12 | - npm install 13 | - npm install -g jshint 14 | test: 15 | override: 16 | - make -k 17 | - make docs 18 | -------------------------------------------------------------------------------- /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 = letter 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | @echo " coverage to run coverage check of the documentation (if enabled)" 49 | 50 | .PHONY: clean 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | .PHONY: html 55 | html: 56 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 57 | @echo 58 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 59 | 60 | .PHONY: dirhtml 61 | dirhtml: 62 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 63 | @echo 64 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 65 | 66 | .PHONY: singlehtml 67 | singlehtml: 68 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 69 | @echo 70 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 71 | 72 | .PHONY: pickle 73 | pickle: 74 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 75 | @echo 76 | @echo "Build finished; now you can process the pickle files." 77 | 78 | .PHONY: json 79 | json: 80 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 81 | @echo 82 | @echo "Build finished; now you can process the JSON files." 83 | 84 | .PHONY: htmlhelp 85 | htmlhelp: 86 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 87 | @echo 88 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 89 | ".hhp project file in $(BUILDDIR)/htmlhelp." 90 | 91 | .PHONY: qthelp 92 | qthelp: 93 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 94 | @echo 95 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 96 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 97 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PROJECT_NAME.qhcp" 98 | @echo "To view the help file:" 99 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PROJECT_NAME.qhc" 100 | 101 | .PHONY: applehelp 102 | applehelp: 103 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 104 | @echo 105 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 106 | @echo "N.B. You won't be able to view it unless you put it in" \ 107 | "~/Library/Documentation/Help or install it in your application" \ 108 | "bundle." 109 | 110 | .PHONY: devhelp 111 | devhelp: 112 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 113 | @echo 114 | @echo "Build finished." 115 | @echo "To view the help file:" 116 | @echo "# mkdir -p $$HOME/.local/share/devhelp/PROJECT_NAME" 117 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PROJECT_NAME" 118 | @echo "# devhelp" 119 | 120 | .PHONY: epub 121 | epub: 122 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 123 | @echo 124 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 125 | 126 | .PHONY: latex 127 | latex: 128 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 129 | @echo 130 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 131 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 132 | "(use \`make latexpdf' here to do that automatically)." 133 | 134 | .PHONY: latexpdf 135 | latexpdf: 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo "Running LaTeX files through pdflatex..." 138 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 139 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 140 | 141 | .PHONY: latexpdfja 142 | latexpdfja: 143 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 144 | @echo "Running LaTeX files through platex and dvipdfmx..." 145 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 146 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 147 | 148 | .PHONY: text 149 | text: 150 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 151 | @echo 152 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 153 | 154 | .PHONY: man 155 | man: 156 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 157 | @echo 158 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 159 | 160 | .PHONY: texinfo 161 | texinfo: 162 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 163 | @echo 164 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 165 | @echo "Run \`make' in that directory to run these through makeinfo" \ 166 | "(use \`make info' here to do that automatically)." 167 | 168 | .PHONY: info 169 | info: 170 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 171 | @echo "Running Texinfo files through makeinfo..." 172 | make -C $(BUILDDIR)/texinfo info 173 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 174 | 175 | .PHONY: gettext 176 | gettext: 177 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 178 | @echo 179 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 180 | 181 | .PHONY: changes 182 | changes: 183 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 184 | @echo 185 | @echo "The overview file is in $(BUILDDIR)/changes." 186 | 187 | .PHONY: linkcheck 188 | linkcheck: 189 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 190 | @echo 191 | @echo "Link check complete; look for any errors in the above output " \ 192 | "or in $(BUILDDIR)/linkcheck/output.txt." 193 | 194 | .PHONY: doctest 195 | doctest: 196 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 197 | @echo "Testing of doctests in the sources finished, look at the " \ 198 | "results in $(BUILDDIR)/doctest/output.txt." 199 | 200 | .PHONY: coverage 201 | coverage: 202 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 203 | @echo "Testing of coverage in the sources finished, look at the " \ 204 | "results in $(BUILDDIR)/coverage/python.txt." 205 | 206 | .PHONY: xml 207 | xml: 208 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 209 | @echo 210 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 211 | 212 | .PHONY: pseudoxml 213 | pseudoxml: 214 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 215 | @echo 216 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 217 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # {{ project_name }} documentation build configuration file, created by 4 | # sphinx-quickstart on Thu Mar 24 09:41:12 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | sys.path.insert(0, os.path.abspath('..')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.autodoc', 33 | 'sphinx.ext.doctest', 34 | 'sphinx.ext.intersphinx', 35 | 'sphinx.ext.todo', 36 | 'sphinx.ext.coverage', 37 | 'sphinx.ext.ifconfig', 38 | 'sphinx.ext.viewcode', 39 | ] 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = ['_templates'] 43 | 44 | # The suffix(es) of source filenames. 45 | # You can specify multiple suffix as a list of string: 46 | # source_suffix = ['.rst', '.md'] 47 | source_suffix = '.rst' 48 | 49 | # The encoding of source files. 50 | #source_encoding = 'utf-8-sig' 51 | 52 | # The master toctree document. 53 | master_doc = 'index' 54 | 55 | # General information about the project. 56 | project = u'{{ project_name }}' 57 | copyright = u'2016, PROJECT_AUTHOR' 58 | author = u'PROJECT_AUTHOR' 59 | 60 | # The version info for the project you're documenting, acts as replacement for 61 | # |version| and |release|, also used in various other places throughout the 62 | # built documents. 63 | # 64 | # The short X.Y version. 65 | version = u'0.1' 66 | # The full version, including alpha/beta/rc tags. 67 | release = u'0.1' 68 | 69 | # The language for content autogenerated by Sphinx. Refer to documentation 70 | # for a list of supported languages. 71 | # 72 | # This is also used if you do content translation via gettext catalogs. 73 | # Usually you set "language" from the command line for these cases. 74 | language = None 75 | 76 | # There are two options for replacing |today|: either, you set today to some 77 | # non-false value, then it is used: 78 | #today = '' 79 | # Else, today_fmt is used as the format for a strftime call. 80 | #today_fmt = '%B %d, %Y' 81 | 82 | # List of patterns, relative to source directory, that match files and 83 | # directories to ignore when looking for source files. 84 | exclude_patterns = ['_build'] 85 | 86 | # The reST default role (used for this markup: `text`) to use for all 87 | # documents. 88 | #default_role = None 89 | 90 | # If true, '()' will be appended to :func: etc. cross-reference text. 91 | #add_function_parentheses = True 92 | 93 | # If true, the current module name will be prepended to all description 94 | # unit titles (such as .. function::). 95 | #add_module_names = True 96 | 97 | # If true, sectionauthor and moduleauthor directives will be shown in the 98 | # output. They are ignored by default. 99 | #show_authors = False 100 | 101 | # The name of the Pygments (syntax highlighting) style to use. 102 | pygments_style = 'sphinx' 103 | 104 | # A list of ignored prefixes for module index sorting. 105 | #modindex_common_prefix = [] 106 | 107 | # If true, keep warnings as "system message" paragraphs in the built documents. 108 | #keep_warnings = False 109 | 110 | # If true, `todo` and `todoList` produce output, else they produce nothing. 111 | todo_include_todos = True 112 | 113 | 114 | # -- Options for HTML output ---------------------------------------------- 115 | 116 | # The theme to use for HTML and HTML Help pages. See the documentation for 117 | # a list of builtin themes. 118 | html_theme = 'alabaster' 119 | 120 | # Theme options are theme-specific and customize the look and feel of a theme 121 | # further. For a list of options available for each theme, see the 122 | # documentation. 123 | #html_theme_options = {} 124 | 125 | # Add any paths that contain custom themes here, relative to this directory. 126 | #html_theme_path = [] 127 | 128 | # The name for this set of Sphinx documents. If None, it defaults to 129 | # " v documentation". 130 | #html_title = None 131 | 132 | # A shorter title for the navigation bar. Default is the same as html_title. 133 | #html_short_title = None 134 | 135 | # The name of an image file (relative to this directory) to place at the top 136 | # of the sidebar. 137 | #html_logo = None 138 | 139 | # The name of an image file (relative to this directory) to use as a favicon of 140 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 141 | # pixels large. 142 | #html_favicon = None 143 | 144 | # Add any paths that contain custom static files (such as style sheets) here, 145 | # relative to this directory. They are copied after the builtin static files, 146 | # so a file named "default.css" will overwrite the builtin "default.css". 147 | html_static_path = ['_static'] 148 | 149 | # Add any extra paths that contain custom files (such as robots.txt or 150 | # .htaccess) here, relative to this directory. These files are copied 151 | # directly to the root of the documentation. 152 | #html_extra_path = [] 153 | 154 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 155 | # using the given strftime format. 156 | #html_last_updated_fmt = '%b %d, %Y' 157 | 158 | # If true, SmartyPants will be used to convert quotes and dashes to 159 | # typographically correct entities. 160 | #html_use_smartypants = True 161 | 162 | # Custom sidebar templates, maps document names to template names. 163 | #html_sidebars = {} 164 | 165 | # Additional templates that should be rendered to pages, maps page names to 166 | # template names. 167 | #html_additional_pages = {} 168 | 169 | # If false, no module index is generated. 170 | #html_domain_indices = True 171 | 172 | # If false, no index is generated. 173 | #html_use_index = True 174 | 175 | # If true, the index is split into individual pages for each letter. 176 | #html_split_index = False 177 | 178 | # If true, links to the reST sources are added to the pages. 179 | #html_show_sourcelink = True 180 | 181 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 182 | #html_show_sphinx = True 183 | 184 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 185 | #html_show_copyright = True 186 | 187 | # If true, an OpenSearch description file will be output, and all pages will 188 | # contain a tag referring to it. The value of this option must be the 189 | # base URL from which the finished HTML is served. 190 | #html_use_opensearch = '' 191 | 192 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 193 | #html_file_suffix = None 194 | 195 | # Language to be used for generating the HTML full-text search index. 196 | # Sphinx supports the following languages: 197 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 198 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 199 | #html_search_language = 'en' 200 | 201 | # A dictionary with options for the search language support, empty by default. 202 | # Now only 'ja' uses this config value 203 | #html_search_options = {'type': 'default'} 204 | 205 | # The name of a javascript file (relative to the configuration directory) that 206 | # implements a search results scorer. If empty, the default will be used. 207 | #html_search_scorer = 'scorer.js' 208 | 209 | # Output file base name for HTML help builder. 210 | htmlhelp_basename = '{{ project_name }}doc' 211 | 212 | # -- Options for LaTeX output --------------------------------------------- 213 | 214 | latex_elements = { 215 | # The paper size ('letterpaper' or 'a4paper'). 216 | #'papersize': 'letterpaper', 217 | 218 | # The font size ('10pt', '11pt' or '12pt'). 219 | #'pointsize': '10pt', 220 | 221 | # Additional stuff for the LaTeX preamble. 222 | #'preamble': '', 223 | 224 | # Latex figure (float) alignment 225 | #'figure_align': 'htbp', 226 | } 227 | 228 | # Grouping the document tree into LaTeX files. List of tuples 229 | # (source start file, target name, title, 230 | # author, documentclass [howto, manual, or own class]). 231 | latex_documents = [ 232 | (master_doc, '{{ project_name }}.tex', u'{{ project_name|title }} Documentation', 233 | u'PROJECT\\_AUTHOR', 'manual'), 234 | ] 235 | 236 | # The name of an image file (relative to this directory) to place at the top of 237 | # the title page. 238 | #latex_logo = None 239 | 240 | # For "manual" documents, if this is true, then toplevel headings are parts, 241 | # not chapters. 242 | #latex_use_parts = False 243 | 244 | # If true, show page references after internal links. 245 | #latex_show_pagerefs = False 246 | 247 | # If true, show URL addresses after external links. 248 | #latex_show_urls = False 249 | 250 | # Documents to append as an appendix to all manuals. 251 | #latex_appendices = [] 252 | 253 | # If false, no module index is generated. 254 | #latex_domain_indices = True 255 | 256 | 257 | # -- Options for manual page output --------------------------------------- 258 | 259 | # One entry per manual page. List of tuples 260 | # (source start file, name, description, authors, manual section). 261 | man_pages = [ 262 | (master_doc, '{{ project_name }}', 263 | u'{{ project_name|title }} Documentation', 264 | [author], 1) 265 | ] 266 | 267 | # If true, show URL addresses after external links. 268 | #man_show_urls = False 269 | 270 | 271 | # -- Options for Texinfo output ------------------------------------------- 272 | 273 | # Grouping the document tree into Texinfo files. List of tuples 274 | # (source start file, target name, title, author, 275 | # dir menu entry, description, category) 276 | texinfo_documents = [ 277 | (master_doc, '{{ project_name }}', u'{{ project_name|title }} Documentation', 278 | author, '{{ project_name }}', 'One line description of project.', 279 | 'Miscellaneous'), 280 | ] 281 | 282 | # Documents to append as an appendix to all manuals. 283 | #texinfo_appendices = [] 284 | 285 | # If false, no module index is generated. 286 | #texinfo_domain_indices = True 287 | 288 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 289 | #texinfo_show_urls = 'footnote' 290 | 291 | # If true, do not generate a @detailmenu in the "Top" node's menu. 292 | #texinfo_no_detailmenu = False 293 | 294 | 295 | # -- Options for Epub output ---------------------------------------------- 296 | 297 | # Bibliographic Dublin Core info. 298 | epub_title = project 299 | epub_author = author 300 | epub_publisher = author 301 | epub_copyright = copyright 302 | 303 | # The basename for the epub file. It defaults to the project name. 304 | #epub_basename = project 305 | 306 | # The HTML theme for the epub output. Since the default themes are not 307 | # optimized for small screen space, using the same theme for HTML and epub 308 | # output is usually not wise. This defaults to 'epub', a theme designed to save 309 | # visual space. 310 | #epub_theme = 'epub' 311 | 312 | # The language of the text. It defaults to the language option 313 | # or 'en' if the language is not set. 314 | #epub_language = '' 315 | 316 | # The scheme of the identifier. Typical schemes are ISBN or URL. 317 | #epub_scheme = '' 318 | 319 | # The unique identifier of the text. This can be a ISBN number 320 | # or the project homepage. 321 | #epub_identifier = '' 322 | 323 | # A unique identification for the text. 324 | #epub_uid = '' 325 | 326 | # A tuple containing the cover image and cover page html template filenames. 327 | #epub_cover = () 328 | 329 | # A sequence of (type, uri, title) tuples for the guide element of content.opf. 330 | #epub_guide = () 331 | 332 | # HTML files that should be inserted before the pages created by sphinx. 333 | # The format is a list of tuples containing the path and title. 334 | #epub_pre_files = [] 335 | 336 | # HTML files that should be inserted after the pages created by sphinx. 337 | # The format is a list of tuples containing the path and title. 338 | #epub_post_files = [] 339 | 340 | # A list of files that should not be packed into the epub file. 341 | epub_exclude_files = ['search.html'] 342 | 343 | # The depth of the table of contents in toc.ncx. 344 | #epub_tocdepth = 3 345 | 346 | # Allow duplicate toc entries. 347 | #epub_tocdup = True 348 | 349 | # Choose between 'default' and 'includehidden'. 350 | #epub_tocscope = 'default' 351 | 352 | # Fix unsupported image types using the Pillow. 353 | #epub_fix_images = False 354 | 355 | # Scale large images. 356 | #epub_max_image_width = 0 357 | 358 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 359 | #epub_show_urls = 'inline' 360 | 361 | # If false, no index is generated. 362 | #epub_use_index = True 363 | 364 | 365 | # Example configuration for intersphinx: refer to the Python standard library. 366 | intersphinx_mapping = {'https://docs.python.org/': None} 367 | 368 | 369 | os.environ['ENVIRONMENT'] = 'production' 370 | os.environ['SECRET_KEY'] = 'foo' 371 | os.environ['DOMAIN'] = 'example.com' 372 | # os.environ['DEFAULT_FROM_EMAIL'] = 'noreply@example.com' 373 | -------------------------------------------------------------------------------- /docs/dev/index.rst: -------------------------------------------------------------------------------- 1 | Develop and deploy {{ project_name }} 2 | ============================================================ 3 | 4 | Contents: 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | provisioning 10 | settings 11 | translation 12 | -------------------------------------------------------------------------------- /docs/dev/provisioning.rst: -------------------------------------------------------------------------------- 1 | Server Provisioning 2 | ======================== 3 | 4 | 5 | Overview 6 | ------------------------ 7 | 8 | {{ project_name|title }} is deployed on the following stack. 9 | 10 | - OS: Ubuntu 18.04 LTS 11 | - Python: 3.7+ 12 | - Database: Postgres 9.3 13 | - Application Server: Gunicorn 14 | - Frontend Server: Nginx 15 | - Cache: Memcached 16 | 17 | These services can configured to run together on a single machine or on different machines. 18 | `Supervisord `_ manages the application server process. 19 | 20 | 21 | .. note:: 22 | 23 | Deploying using Dokku is an alternative to the information on this page. 24 | See the README to get started. 25 | 26 | 27 | Provisioning Options 28 | -------------------- 29 | 30 | Caktus prefers one of two provisioning options for new projects, `Tequila`_ or `Dokku`_. 31 | 32 | Dokku is a good option for small projects, early projects in need of a staging machine, 33 | or non-client projects run as experiments or side projects. 34 | 35 | Tequila, built on Ansible, should be used for any project that will need more than one machine 36 | and more complex provisioning and deployment configurations. 37 | 38 | Dokku 39 | ''''' 40 | 41 | The configuration requirements for Dokku are very small, so they are included in the 42 | project template by default. You can even use them in addition to Tequila, if you'd like, 43 | or transition from Dokku to Tequila after an initial prototyping phase of a project. 44 | 45 | Primarily Dokku is configured by the `runtime.txt` and `app.json` files. The `predeploy.sh` and 46 | `postdeploy.sh` scripts are also used as part of the Dokku deploys. In order to setup a new project 47 | just create a new Dokku app on your server and a new database using the Dokku Postgres plugin:: 48 | 49 | ssh dokku@dokku.me apps:create my-new-app 50 | ssh dokku@dokku.me postgres:create my-new-app-db 51 | postgres:link my-new-app-db my-new-app 52 | 53 | And configure the Dokku server as a remote for your git repository:: 54 | 55 | git remote add dokku dokku@dokku.me:my-new-app 56 | 57 | Now, simply pushing to this remote will deploy your application's master branch:: 58 | 59 | git push dokku 60 | 61 | Tequila 62 | ''''''' 63 | 64 | You can read about how to setup Tequila for a new project from 65 | `Tequila Project Setup ` 66 | documentation, which will walk you through adding Tequila to any Django project, including one 67 | created from this project template. 68 | -------------------------------------------------------------------------------- /docs/dev/settings.rst: -------------------------------------------------------------------------------- 1 | Project settings 2 | ================ 3 | 4 | This only lists settings that have docstrings. See the relevant 5 | settings files for the complete settings, or run:: 6 | 7 | $ python manage.py diffsettings 8 | 9 | Base settings 10 | ------------- 11 | 12 | .. automodule:: {{ project_name }}.settings.base 13 | :members: 14 | 15 | Deploy settings 16 | --------------- 17 | 18 | .. automodule:: {{ project_name }}.settings.deploy 19 | :members: 20 | 21 | Development settings 22 | -------------------- 23 | 24 | .. automodule:: {{ project_name }}.settings.dev 25 | :members: 26 | -------------------------------------------------------------------------------- /docs/dev/translation.rst: -------------------------------------------------------------------------------- 1 | Translation 2 | =========== 3 | 4 | Resources 5 | --------- 6 | 7 | This project uses the standard `Django translation mechanisms 8 | `_. 9 | 10 | We use `Transifex `_ to make it easy for translators 11 | to convert the English strings in our interface to proper translated strings in 12 | other languages. 13 | 14 | 15 | What goes in version control? 16 | ----------------------------- 17 | 18 | While the ``.po`` files can be regenerated easily by running ``make 19 | makemessages`` again for English or ``make pullmessages`` for the translated 20 | languages, we still store them in Git to make it easier to keep an eye on 21 | changes, and revert if needed. That way we are less likely to accidentally make 22 | a mistake and delete huge swaths of messages without noticing it. 23 | 24 | We also store the ``.mo`` files in Git because those are what Django gets the 25 | translated messages from at runtime. 26 | 27 | 28 | First time setup 29 | ---------------- 30 | 31 | Steps 1 and 2 only need to be done once. Step 3 would only need to be repeated 32 | if you were to add a new PO file to be translated. An example would be if you 33 | were to add frontend JS translations. 34 | 35 | 1. Create a project on Transifex. This documentation will assume that you named 36 | it ``{{ project_name }}``. 37 | 38 | #. In the repo, create your Transifex config file:: 39 | 40 | tx init 41 | 42 | #. Tell Transifex where your files are, and how to link them to Transifex:: 43 | 44 | tx set --auto-local -r {{ project_name }}.djangopo \ 45 | 'locale//LC_MESSAGES/django.po' \ 46 | --source-lang en --type PO --execute 47 | 48 | #. Commit this to the repo:: 49 | 50 | git commit -m "Setup Transifex translation" .tx 51 | git push 52 | 53 | 54 | Updating messages on Transifex 55 | ------------------------------ 56 | 57 | Anytime there have been changes to the messages in the code or templates, 58 | a developer should update the messages on Transifex as follows: 59 | 60 | 1. Make sure you have the latest code from develop:: 61 | 62 | git checkout develop 63 | git pull 64 | 65 | #. regenerate the English (only) .po files:: 66 | 67 | make makemessages 68 | 69 | #. Run ``git diff`` and make sure the changes look reasonable. 70 | 71 | #. If so, commit the updated .po file to develop and push it upstream:: 72 | 73 | git commit -m "Updated messages" locale/en/LC_MESSAGES/*.po 74 | git push 75 | 76 | #. `Push 77 | `_ 78 | the updated source file to Transifex:: 79 | 80 | make pushmessages 81 | 82 | 83 | Updating translations from Transifex 84 | ------------------------------------ 85 | 86 | Anytime translations on Transifex have been updated, someone should update our 87 | translation files on the develop branch as follows: 88 | 89 | 1. Make sure you have the latest code from develop:: 90 | 91 | git checkout develop 92 | git pull 93 | 94 | #. `Pull `_ 95 | the updated .po files from Transifex:: 96 | 97 | make pullmessages 98 | 99 | #. Use ``git diff`` to see if any translations have actually changed. If not, 100 | you can stop here. 101 | 102 | #. Look at the diffs to see if the changes look reasonable. E.g. if translations 103 | have vanished, figure out why before proceeding. 104 | 105 | #. Compile the messages to .mo files:: 106 | 107 | make compilemessages 108 | 109 | If you get any errors due to badly formatted translations, open issues on 110 | Transifex and work with the translators to get them fixed, then start this 111 | process over. 112 | 113 | #. Run your test suite one more time:: 114 | 115 | python manage.py test 116 | 117 | #. Commit and push the changes:: 118 | 119 | git commit -m "Updated translations" locale/*/LC_MESSAGES/*.po locale/*/LC_MESSAGES/*.mo 120 | git push 121 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. PROJECT_NAME documentation master file, created by 2 | sphinx-quickstart on Thu Mar 24 09:41:12 2016. 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 {{ project_name|title }}'s documentation! 7 | ============================================================ 8 | 9 | Contents: 10 | 11 | .. toctree:: 12 | :maxdepth: 2 13 | 14 | userguide/index 15 | dev/index 16 | 17 | Indices and tables 18 | ================== 19 | 20 | * :ref:`genindex` 21 | * :ref:`modindex` 22 | * :ref:`search` 23 | 24 | -------------------------------------------------------------------------------- /docs/userguide/index.rst: -------------------------------------------------------------------------------- 1 | User Guide for {{ project_name }} 2 | ============================================================ 3 | 4 | To be written. 5 | 6 | Contents: 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | -------------------------------------------------------------------------------- /dokku_first_deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | # Script to run for the first Dokku deploy 5 | # It tries to be idempotent where possible, but it's not always possible. 6 | # After running this once, later deploys can be done just by pushing master to Dokku: 7 | # 8 | # git push dokku master 9 | # 10 | # To deploy other branches, see http://dokku.viewdocs.io/dokku/deployment/application-deployment/#deploying-non-master-branch 11 | 12 | # Allow overriding project name on command line 13 | if [ "$1" != "" ] ; then 14 | PROJECT="$1" 15 | else 16 | PROJECT="{{ project_name }}" 17 | fi 18 | 19 | if ! git status >/dev/null ; then 20 | echo "The project must be checked into git before continuing" 21 | exit 1 22 | fi 23 | 24 | DOKKU_SERVER=dokku 25 | 26 | if ! ssh $DOKKU_SERVER version ; then 27 | echo "This script assumes 'ssh'"$DOKKU_SERVER"' will connect as dokku to the dokku server." 28 | echo "Either edit the top of this script, or add something like this to your ~/.ssh/config file:" 29 | cat <<_EOF_ 30 | Host $DOKKU_SERVER 31 | Hostname my.dokku.server.tld 32 | User dokku 33 | RequestTTY yes 34 | _EOF_ 35 | exit 1 36 | fi 37 | 38 | dokku() { ssh $DOKKU_SERVER "$@"; } 39 | 40 | dokku apps:report $PROJECT || dokku apps:create $PROJECT 41 | 42 | STORAGE=/var/lib/dokku/data/storage/$PROJECT 43 | dokku storage:list $PROJECT | grep --quiet $STORAGE || dokku storage:mount $PROJECT $STORAGE:/storage 44 | dokku config:set $PROJECT MEDIA_ROOT=/storage/media MEDIA_URL=/media ENVIRONMENT=production DOMAIN=$PROJECT.$(dokku domains:report $PROJECT --domains-global-vhosts) 45 | 46 | # Create and link to database 47 | dokku postgres:info $PROJECT-database || dokku postgres:create $PROJECT-database 48 | dokku postgres:info $PROJECT-database | grep Links: | grep --quiet $PROJECT || dokku postgres:link $PROJECT-database $PROJECT 49 | 50 | # Create a secret key, but only if there's not one already. 51 | dokku config:get $PROJECT DJANGO_SECRET_KEY >/dev/null || dokku config:set --no-restart $PROJECT DJANGO_SECRET_KEY=$(make generate-secret) 52 | 53 | # Create remote 54 | git remote | grep dokku || git remote add dokku $DOKKU_SERVER:$PROJECT 55 | 56 | # PUSH! First deploy 57 | git push dokku master 58 | 59 | # Set up letsencrypt - assumes DOKKU_LETSENCRYPT_EMAIL is already set globally on the Dokku server, or else it'll fail 60 | dokku letsencrypt $PROJECT 61 | dokku letsencrypt:cron-job --add $PROJECT 62 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var source = require('vinyl-source-stream'); // Used to stream bundle for further handling 3 | var browserify = require('browserify'); 4 | var watchify = require('watchify'); 5 | var babelify = require('babelify'); 6 | var gulpif = require('gulp-if'); 7 | var uglify = require('gulp-uglify'); 8 | var streamify = require('gulp-streamify'); 9 | var notify = require('gulp-notify'); 10 | var concat = require('gulp-concat'); 11 | var cleancss = require('gulp-clean-css'); 12 | var log = require('fancy-log'); 13 | var rename = require("gulp-rename"); 14 | var less = require('gulp-less'); 15 | var glob = require('glob'); 16 | var path = require('path'); 17 | var livereload = require('gulp-livereload'); 18 | var modernizr = require('gulp-modernizr'); 19 | var mocha = require('gulp-mocha'); 20 | var istanbul = require('gulp-istanbul'); 21 | var isparta = require('isparta'); 22 | var coverageEnforcer = require('gulp-istanbul-enforcer'); 23 | // Note: this touch only updates the modtime of an existing file, but will never 24 | // create an empty file (unlike the command-line "touch"). 25 | var touch = require('gulp-touch-fd'); 26 | var spawn = require('child_process').spawn; 27 | var argv = require('yargs') 28 | .default('port', 8000) 29 | .default('address', 'localhost') 30 | .argv; 31 | 32 | // External dependencies you do not want to rebundle while developing, 33 | // but include in your application deployment 34 | var dependencies = [ 35 | ]; 36 | 37 | var options = { 38 | src: './{{ project_name }}/static/js/index.js', 39 | dest: './{{ project_name }}/static/js/', 40 | 41 | modernizr: { 42 | src: './{{ project_name }}/static/js/index.js', 43 | dest: './{{ project_name }}/static/libs/', 44 | }, 45 | 46 | css: { 47 | src: './{{ project_name }}/static/less/index.less', 48 | watch: './{{ project_name }}/static/less/**/*.less', 49 | dest: './{{ project_name }}/static/css/' 50 | }, 51 | development: false 52 | } 53 | 54 | if (argv._ && argv._[0] === 'deploy') { 55 | options.development = false 56 | } else { 57 | options.development = true 58 | } 59 | 60 | if (options.development) { 61 | console.log("Building for development") 62 | delete process.env['NODE_ENV']; 63 | } else { 64 | console.log("Building for production") 65 | process.env['NODE_ENV'] = 'production'; 66 | } 67 | 68 | gulp.task('modernizr', function() { 69 | return gulp.src(options.modernizr.src) 70 | .pipe(modernizr()) 71 | .pipe(gulpif(!options.development, streamify(uglify()))) 72 | .pipe(gulp.dest(options.modernizr.dest)) 73 | .pipe(touch()) 74 | }) 75 | 76 | var browserifyTask = function () { 77 | 78 | // Our app bundler 79 | var appBundler = browserify({ 80 | entries: [options.src], // Only need initial file, browserify finds the rest 81 | transform: [babelify], // We want to convert JSX to normal javascript 82 | debug: options.development, // Gives us sourcemapping 83 | cache: {}, packageCache: {}, fullPaths: options.development // Requirement of watchify 84 | }); 85 | 86 | // We set our dependencies as externals on our app bundler when developing 87 | (options.development ? dependencies : []).forEach(function (dep) { 88 | appBundler.external(dep); 89 | }); 90 | 91 | // The rebundle process 92 | var rebundle = function () { 93 | var start = Date.now(); 94 | console.log('Building APP bundle'); 95 | return appBundler.bundle() 96 | .on('error', log) 97 | .pipe(source('index.js')) 98 | .pipe(gulpif(!options.development, streamify(uglify()))) 99 | .pipe(rename('bundle.js')) 100 | .pipe(gulp.dest(options.dest)) 101 | .pipe(gulpif(options.development, livereload())) 102 | .pipe(touch()) 103 | .pipe(notify(function () { 104 | console.log('APP bundle built in ' + (Date.now() - start) + 'ms'); 105 | })); 106 | }; 107 | 108 | // Fire up Watchify when developing 109 | if (options.development) { 110 | var watcher = watchify(appBundler); 111 | watcher.on('update', rebundle); 112 | } 113 | 114 | // We create a separate bundle for our dependencies as they 115 | // should not rebundle on file changes. This only happens when 116 | // we develop. When deploying the dependencies will be included 117 | // in the application bundle 118 | if (options.development) { 119 | 120 | var vendorsBundler = browserify({ 121 | debug: true, 122 | require: dependencies 123 | }); 124 | 125 | // Run the vendor bundle 126 | var start = new Date(); 127 | console.log('Building VENDORS bundle'); 128 | vendorsBundler.bundle() 129 | .on('error', log) 130 | .pipe(source('vendors.js')) 131 | .pipe(gulpif(!options.development, streamify(uglify()))) 132 | .pipe(gulp.dest(options.dest)) 133 | .pipe(touch()) 134 | .pipe(notify(function () { 135 | console.log('VENDORS bundle built in ' + (Date.now() - start) + 'ms'); 136 | })); 137 | } 138 | 139 | return rebundle(); 140 | }; 141 | gulp.task('browserify', gulp.series('modernizr', browserifyTask)); 142 | 143 | var cssTask = function () { 144 | var lessOpts = { 145 | relativeUrls: true, 146 | }; 147 | if (options.development) { 148 | var run = function () { 149 | var start = Date.now(); 150 | console.log('Building CSS bundle'); 151 | return gulp.src(options.css.src) 152 | .pipe(gulpif(options.development, livereload())) 153 | .pipe(concat('index.less')) 154 | .pipe(less(lessOpts)) 155 | .pipe(rename('bundle.css')) 156 | .pipe(gulp.dest(options.css.dest)) 157 | .pipe(touch()) 158 | .pipe(notify(function () { 159 | console.log('CSS bundle built in ' + (Date.now() - start) + 'ms'); 160 | })); 161 | }; 162 | gulp.watch(options.css.watch, run); 163 | return run(); 164 | } else { 165 | return gulp.src(options.css.src) 166 | .pipe(concat('index.less')) 167 | .pipe(less(lessOpts)) 168 | .pipe(rename('bundle.css')) 169 | .pipe(cleancss()) 170 | .pipe(gulp.dest(options.css.dest)) 171 | .pipe(touch()) 172 | } 173 | }; 174 | gulp.task('css', cssTask); 175 | 176 | gulp.task('rebuild', gulp.parallel('css', 'browserify')) 177 | 178 | function start_dev_server(done) { 179 | console.log("Starting Django runserver http://"+argv.address+":"+argv.port+"/"); 180 | var args = ["manage.py", "runserver", argv.address+":"+argv.port]; 181 | // Newer versions of npm mess with the PATH, sometimes putting /usr/bin at the front, 182 | // so make sure we invoke the python from our virtual env explicitly. 183 | var python = process.env['VIRTUAL_ENV'] + '/bin/python'; 184 | var runserver = spawn(python, args, { 185 | stdio: "inherit", 186 | }); 187 | runserver.on('close', function(code) { 188 | if (code !== 0) { 189 | console.error('Django runserver exited with error code: ' + code); 190 | } else { 191 | console.log('Django runserver exited normally.'); 192 | } 193 | }); 194 | done(); 195 | } 196 | gulp.task('start_dev_server', gulp.series('rebuild', start_dev_server)) 197 | 198 | // Starts our development workflow 199 | gulp.task('default', gulp.series('start_dev_server', function (done) { 200 | livereload.listen(); 201 | done(); 202 | })); 203 | 204 | gulp.task('deploy', gulp.series('rebuild')) 205 | 206 | gulp.task('test', gulp.series(function () { 207 | require('babel-core/register'); 208 | return gulp 209 | .src('./{{ project_name }}/static/js/app/**/*.js') 210 | .pipe(istanbul({ 211 | instrumenter: isparta.Instrumenter 212 | , includeUntested: true 213 | })) 214 | .pipe(istanbul.hookRequire()) 215 | .on('finish', function () { 216 | gulp 217 | .src('./{{ project_name }}/static/js/test/**/test_*.js', {read: false}) 218 | .pipe(mocha({ 219 | require: [ 220 | 'jsdom-global/register' 221 | ] 222 | })) 223 | .pipe(istanbul.writeReports({ 224 | dir: './coverage/' 225 | , reportOpts: { 226 | dir: './coverage/' 227 | } 228 | , reporters: [ 229 | 'text' 230 | , 'text-summary' 231 | , 'json' 232 | , 'html' 233 | ] 234 | })) 235 | .pipe(coverageEnforcer({ 236 | thresholds: { 237 | statements: 80 238 | , branches: 50 239 | , lines: 80 240 | , functions: 50 241 | } 242 | , coverageDirectory: './coverage/' 243 | , rootDirectory: '' 244 | })) 245 | ; 246 | }) 247 | ; 248 | })); 249 | -------------------------------------------------------------------------------- /locale/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caktus/django-project-template/644148f7327a7bb54e52d3de8a7fe3c03c674b20/locale/.gitkeep -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | from {{ project_name }} import load_env 6 | 7 | load_env.load_env() 8 | 9 | if __name__ == "__main__": 10 | if 'DATABASE_URL' in os.environ: 11 | # Dokku or similar 12 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings.deploy") 13 | else: 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings") 15 | 16 | from django.core.management import execute_from_command_line 17 | 18 | execute_from_command_line(sys.argv) 19 | -------------------------------------------------------------------------------- /nginx.conf.sigil: -------------------------------------------------------------------------------- 1 | # This is run through sigil and installed as the nginx config for this application 2 | # See http://dokku.viewdocs.io/dokku~v0.10.5/configuration/nginx/ 3 | 4 | # This started as the template from dokku v0.10.5 from https://raw.githubusercontent.com/dokku/dokku/v0.10.5/plugins/nginx-vhosts/templates/nginx.conf.sigil 5 | # I've tried to clearly mark the changes so that it won't be too hard to merge in later versions. 6 | 7 | 8 | {{ range $port_map := .PROXY_PORT_MAP | split " " }} 9 | {{ $port_map_list := $port_map | split ":" }} 10 | {{ $scheme := index $port_map_list 0 }} 11 | {{ $listen_port := index $port_map_list 1 }} 12 | {{ $upstream_port := index $port_map_list 2 }} 13 | 14 | {{ if eq $scheme "http" }} 15 | server { 16 | listen [::]:{{ $listen_port }}; 17 | listen {{ $listen_port }}; 18 | {{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }}; {{ end }} 19 | access_log /var/log/nginx/{{ $.APP }}-access.log; 20 | error_log /var/log/nginx/{{ $.APP }}-error.log; 21 | {{ if (and (eq $listen_port "80") ($.SSL_INUSE)) }} 22 | return 301 https://$host:{{ $.NGINX_SSL_PORT }}$request_uri; 23 | {{ else }} 24 | ############## NEXT PART ADDED FOR django-project-template ##################################### 25 | location {{ var "MEDIA_URL" }} { 26 | root /var/lib/dokku/data/storage/{{ $.APP }}/media; 27 | } 28 | ############## END OF PART ADDED FOR django-project-template ################################### 29 | location / { 30 | 31 | gzip on; 32 | gzip_min_length 1100; 33 | gzip_buffers 4 32k; 34 | gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml font/truetype application/x-font-ttf font/opentype application/vnd.ms-fontobject image/svg+xml; 35 | gzip_vary on; 36 | gzip_comp_level 6; 37 | 38 | proxy_pass http://{{ $.APP }}-{{ $upstream_port }}; 39 | proxy_http_version 1.1; 40 | proxy_set_header Upgrade $http_upgrade; 41 | proxy_set_header Connection "upgrade"; 42 | proxy_set_header Host $http_host; 43 | proxy_set_header X-Forwarded-Proto $scheme; 44 | proxy_set_header X-Forwarded-For $remote_addr; 45 | proxy_set_header X-Forwarded-Port $server_port; 46 | proxy_set_header X-Request-Start $msec; 47 | } 48 | include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf; 49 | 50 | error_page 400 401 402 403 405 406 407 408 409 410 411 412 413 414 415 416 417 418 420 422 423 424 426 428 429 431 444 449 450 451 /400-error.html; 51 | location /400-error.html { 52 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 53 | internal; 54 | } 55 | 56 | error_page 404 /404-error.html; 57 | location /404-error.html { 58 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 59 | internal; 60 | } 61 | 62 | error_page 500 501 502 503 504 505 506 507 508 509 510 511 /500-error.html; 63 | location /500-error.html { 64 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 65 | internal; 66 | } 67 | {{ end }} 68 | } 69 | {{ else if eq $scheme "https"}} 70 | server { 71 | listen [::]:{{ $listen_port }} ssl {{ if eq $.HTTP2_SUPPORTED "true" }}http2{{ else if eq $.SPDY_SUPPORTED "true" }}spdy{{ end }}; 72 | listen {{ $listen_port }} ssl {{ if eq $.HTTP2_SUPPORTED "true" }}http2{{ else if eq $.SPDY_SUPPORTED "true" }}spdy{{ end }}; 73 | {{ if $.SSL_SERVER_NAME }}server_name {{ $.SSL_SERVER_NAME }}; {{ end }} 74 | {{ if $.NOSSL_SERVER_NAME }}server_name {{ $.NOSSL_SERVER_NAME }}; {{ end }} 75 | access_log /var/log/nginx/{{ $.APP }}-access.log; 76 | error_log /var/log/nginx/{{ $.APP }}-error.log; 77 | 78 | ssl_certificate {{ $.APP_SSL_PATH }}/server.crt; 79 | ssl_certificate_key {{ $.APP_SSL_PATH }}/server.key; 80 | ssl_protocols TLSv1.2; 81 | ssl_prefer_server_ciphers on; 82 | 83 | keepalive_timeout 70; 84 | {{ if and (eq $.SPDY_SUPPORTED "true") (ne $.HTTP2_SUPPORTED "true") }}add_header Alternate-Protocol {{ $.NGINX_SSL_PORT }}:npn-spdy/2;{{ end }} 85 | 86 | ############## NEXT PART ADDED FOR django-project-template ##################################### 87 | location {{ var "MEDIA_URL" }} { 88 | root /var/lib/dokku/data/storage/{{ $.APP }}/media; 89 | } 90 | ############## END OF PART ADDED FOR django-project-template ################################### 91 | 92 | location / { 93 | 94 | gzip on; 95 | gzip_min_length 1100; 96 | gzip_buffers 4 32k; 97 | gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/x-javascript application/json application/xml application/rss+xml font/truetype application/x-font-ttf font/opentype application/vnd.ms-fontobject image/svg+xml; 98 | gzip_vary on; 99 | gzip_comp_level 6; 100 | 101 | proxy_pass http://{{ $.APP }}-{{ $upstream_port }}; 102 | proxy_http_version 1.1; 103 | proxy_set_header Upgrade $http_upgrade; 104 | proxy_set_header Connection "upgrade"; 105 | proxy_set_header Host $http_host; 106 | proxy_set_header X-Forwarded-Proto $scheme; 107 | proxy_set_header X-Forwarded-For $remote_addr; 108 | proxy_set_header X-Forwarded-Port $server_port; 109 | proxy_set_header X-Request-Start $msec; 110 | } 111 | include {{ $.DOKKU_ROOT }}/{{ $.APP }}/nginx.conf.d/*.conf; 112 | 113 | error_page 400 401 402 403 405 406 407 408 409 410 411 412 413 414 415 416 417 418 420 422 423 424 426 428 429 431 444 449 450 451 /400-error.html; 114 | location /400-error.html { 115 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 116 | internal; 117 | } 118 | 119 | error_page 404 /404-error.html; 120 | location /404-error.html { 121 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 122 | internal; 123 | } 124 | 125 | error_page 500 501 502 503 504 505 506 507 508 509 510 511 /500-error.html; 126 | location /500-error.html { 127 | root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; 128 | internal; 129 | } 130 | } 131 | {{ end }}{{ end }} 132 | 133 | {{ if $.DOKKU_APP_LISTENERS }} 134 | {{ range $upstream_port := $.PROXY_UPSTREAM_PORTS | split " " }} 135 | upstream {{ $.APP }}-{{ $upstream_port }} { 136 | {{ range $listeners := $.DOKKU_APP_LISTENERS | split " " }} 137 | {{ $listener_list := $listeners | split ":" }} 138 | {{ $listener_ip := index $listener_list 0 }} 139 | server {{ $listener_ip }}:{{ $upstream_port }};{{ end }} 140 | } 141 | {{ end }}{{ end }} 142 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "{{ project_name }}", 3 | "version": "0.0.0", 4 | "description": "The {{ project_name|title }} Project", 5 | "main": "", 6 | "engines": { 7 | "node": ">=10.15 <11" 8 | }, 9 | "directories": { 10 | "doc": "docs" 11 | }, 12 | "scripts": { 13 | "test": "NODE_ENV=test ./node_modules/.bin/gulp test", 14 | "build": "./node_modules/.bin/gulp deploy", 15 | "dev": "./node_modules/.bin/gulp" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git@github.com:[ORGANIZATION]/{{ project_name }}.git" 20 | }, 21 | "author": "", 22 | "license": "BSD", 23 | "bugs": { 24 | "url": "https://github.com/[ORGANIZATION]/{{ project_name }}/issues" 25 | }, 26 | "homepage": "https://github.com/[ORGANIZATION]/{{ project_name }}#readme", 27 | "dependencies": { 28 | "babel-core": "^6.26.3", 29 | "babel-eslint": "^10.0.3", 30 | "babel-plugin-rewire": "^1.2.0", 31 | "babel-plugin-transform-react-jsx": "^6.24.1", 32 | "babel-preset-es2015": "^6.24.1", 33 | "babelify": "^8.0.0", 34 | "bootstrap": "^4.3.1", 35 | "browserify": "^16.5.0", 36 | "chai": "^4.2.0", 37 | "eslint": "^6.5.0", 38 | "fancy-log": "^1.3.3", 39 | "file-exists": "^5.0.1", 40 | "flux": "^3.1.3", 41 | "glob": "^7.1.4", 42 | "gulp": "^4.0.2", 43 | "gulp-clean-css": "^4.2.0", 44 | "gulp-concat": "^2.6.1", 45 | "gulp-connect": "^5.7.0", 46 | "gulp-if": "^3.0.0", 47 | "gulp-istanbul": "^1.1.3", 48 | "gulp-istanbul-enforcer": "^1.0.3", 49 | "gulp-jasmine2-phantomjs": "^1.0.1", 50 | "gulp-less": "^4.0.1", 51 | "gulp-livereload": "^4.0.2", 52 | "gulp-mocha": "^7.0.1", 53 | "gulp-modernizr": "3.3.0", 54 | "gulp-notify": "^3.2.0", 55 | "gulp-rename": "^1.4.0", 56 | "gulp-streamify": "1.0.2", 57 | "gulp-touch-fd": "github:funkedigital/gulp-touch-fd", 58 | "gulp-uglify": "^3.0.2", 59 | "immutable": "^3.8.2", 60 | "isparta": "^4.1.1", 61 | "jquery": "^3.4.1", 62 | "jsdom": "^15.1.1", 63 | "jsdom-global": "^3.0.2", 64 | "less": "^3.10.3", 65 | "mocha": "^6.2.1", 66 | "mocha-jsdom": "^2.0.0", 67 | "react": "^16.10.1", 68 | "react-addons-test-utils": "^15.6.2", 69 | "react-dom": "^16.10.1", 70 | "react-functional": "^3.0.2", 71 | "sinon": "^7.5.0", 72 | "vinyl-source-stream": "^2.0.0", 73 | "watchify": "^3.11.1", 74 | "yargs": "^14.0.0" 75 | }, 76 | "browserify": { 77 | "transform": [ 78 | [ 79 | "babelify", 80 | { 81 | "presets": [ 82 | "es2015" 83 | ] 84 | } 85 | ] 86 | ] 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /project.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | sudo: false 4 | 5 | python: 6 | - "3.7" 7 | 8 | # Match postgresql version to that in conf/pillar/project.sls 9 | services: 10 | - postgresql 11 | 12 | cache: 13 | directories: 14 | - $HOME/.cache/pip 15 | - $HOME/.npm 16 | - $HOME/.nvm 17 | - $HOME/.cache/node_modules 18 | - $HOME/.cache/venvs 19 | 20 | env: 21 | - DJANGO_SETTINGS_MODULE="{{ project_name }}.settings.dev" 22 | 23 | install: 24 | - pip install -U pip wheel 25 | - pip install -U -r requirements/dev.txt 26 | - nvm install 10.16.0 27 | - nvm use 10.16 28 | - npm install 29 | 30 | before_script: 31 | - createdb --encoding=UTF-8 {{ project_name }} --username=postgres --owner=`whoami` 32 | # Uncomment for Requires.IO pushes of develop and master merges (not pull-request builds) 33 | # Requires the $REQUIRES_IO_TOKEN environment variable defined at Travis CI for this project 34 | # See developer documentation section on depdency tracking for more information. 35 | # - if [ "$TRAVIS_PULL_REQUEST" == "false" ] ; then requires.io update-branch -t $REQUIRES_IO_TOKEN -r rescueid -n $(echo $TRAVIS_BRANCH | sed "s|/|__|g") . ; fi 36 | # Uncomment for PostGIS support 37 | # - psql service_info -c "CREATE EXTENSION postgis;" -U postgres 38 | 39 | script: 40 | - python manage.py makemigrations --dry-run | grep 'No changes detected' || (echo 'There are changes which require migrations.' && exit 1) 41 | - coverage run manage.py test 42 | - coverage report -m --fail-under 80 43 | - flake8 . 44 | - make docs 45 | - npm test 46 | -------------------------------------------------------------------------------- /project_name/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caktus/django-project-template/644148f7327a7bb54e52d3de8a7fe3c03c674b20/project_name/__init__.py -------------------------------------------------------------------------------- /project_name/load_env.py: -------------------------------------------------------------------------------- 1 | from os.path import dirname, join 2 | import dotenv 3 | 4 | 5 | def load_env(): 6 | "Get the path to the .env file and load it." 7 | project_dir = dirname(dirname(__file__)) 8 | dotenv.read_dotenv(join(project_dir, '.env')) 9 | -------------------------------------------------------------------------------- /project_name/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/caktus/django-project-template/644148f7327a7bb54e52d3de8a7fe3c03c674b20/project_name/settings/__init__.py -------------------------------------------------------------------------------- /project_name/settings/base.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for {{ project_name }} project. 3 | 4 | Generated by 'django-admin startproject' using Django {{ django_version }}. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/{{ docs_version }}/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/ 11 | """ 12 | import os 13 | 14 | BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) 15 | PROJECT_ROOT = os.path.abspath(os.path.join(BASE_DIR, os.pardir)) 16 | 17 | # SECURITY WARNING: don't run with debug turned on in production! 18 | DEBUG = True 19 | 20 | ADMINS = ( 21 | # ('Your Name', 'your_email@example.com'), 22 | ) 23 | 24 | # Application definition 25 | 26 | INSTALLED_APPS = [ 27 | 'django.contrib.auth', 28 | 'django.contrib.contenttypes', 29 | 'django.contrib.sessions', 30 | 'django.contrib.messages', 31 | 'django.contrib.staticfiles', 32 | 'django.contrib.admin', 33 | 'django.contrib.humanize', 34 | 'django.contrib.sitemaps', 35 | ] 36 | 37 | MIDDLEWARE = [ 38 | 'django.middleware.security.SecurityMiddleware', 39 | 'django.contrib.sessions.middleware.SessionMiddleware', 40 | 'django.middleware.locale.LocaleMiddleware', 41 | 'django.middleware.common.CommonMiddleware', 42 | 'django.middleware.csrf.CsrfViewMiddleware', 43 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 44 | 'django.contrib.messages.middleware.MessageMiddleware', 45 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 46 | ] 47 | 48 | 49 | ROOT_URLCONF = '{{ project_name }}.urls' 50 | 51 | TEMPLATES = [ 52 | { 53 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 54 | 'DIRS': [ 55 | os.path.join(BASE_DIR, 'templates'), 56 | ], 57 | 'APP_DIRS': True, 58 | 'OPTIONS': { 59 | 'context_processors': [ 60 | 'django.contrib.auth.context_processors.auth', 61 | 'django.template.context_processors.debug', 62 | 'django.template.context_processors.i18n', 63 | 'django.template.context_processors.tz', 64 | 'django.template.context_processors.request', 65 | 'django.contrib.messages.context_processors.messages', 66 | 'dealer.contrib.django.context_processor', 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = '{{ project_name }}.wsgi.application' 73 | 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#databases 77 | 78 | DATABASES = { 79 | 'default': { 80 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 81 | 'NAME': '{{ project_name }}', 82 | 'USER': '', 83 | 'PASSWORD': '', 84 | 'HOST': '', 85 | 'PORT': '', 86 | } 87 | } 88 | 89 | # Absolute filesystem path to the directory that will hold user-uploaded files. 90 | # Example: "/home/media/media.lawrence.com/media/" 91 | MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'public', 'media') 92 | 93 | # URL that handles the media served from MEDIA_ROOT. Make sure to use a 94 | # trailing slash. 95 | # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" 96 | MEDIA_URL = '/media/' 97 | 98 | LOGGING = { 99 | 'version': 1, 100 | 'disable_existing_loggers': False, 101 | 'filters': { 102 | 'require_debug_false': { 103 | '()': 'django.utils.log.RequireDebugFalse' 104 | } 105 | }, 106 | 'formatters': { 107 | 'basic': { 108 | 'format': '%(asctime)s %(name)-20s %(levelname)-8s %(message)s', 109 | }, 110 | }, 111 | 'handlers': { 112 | 'mail_admins': { 113 | 'level': 'ERROR', 114 | 'filters': ['require_debug_false'], 115 | 'class': 'django.utils.log.AdminEmailHandler' 116 | }, 117 | 'console': { 118 | 'level': 'INFO', 119 | 'class': 'logging.StreamHandler', 120 | 'formatter': 'basic', 121 | }, 122 | }, 123 | 'loggers': { 124 | 'django.request': { 125 | 'handlers': ['mail_admins'], 126 | 'level': 'ERROR', 127 | 'propagate': True, 128 | }, 129 | 'django.security': { 130 | 'handlers': ['mail_admins'], 131 | 'level': 'ERROR', 132 | 'propagate': True, 133 | }, 134 | }, 135 | 'root': { 136 | 'handlers': ['console', ], 137 | 'level': 'INFO', 138 | }, 139 | } 140 | 141 | # Internationalization 142 | # https://docs.djangoproject.com/en/{{ docs_version }}/topics/i18n/ 143 | 144 | # Language code for this installation. All choices can be found here: 145 | # http://www.i18nguy.com/unicode/language-identifiers.html 146 | LANGUAGE_CODE = 'en-us' 147 | LOCALE_PATHS = (os.path.join(PROJECT_ROOT, 'locale'), ) 148 | 149 | # Local time zone for this installation. Choices can be found here: 150 | # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name 151 | # although not all choices may be available on all operating systems. 152 | # On Unix systems, a value of None will cause Django to use the same 153 | # timezone as the operating system. 154 | # If running in a Windows environment this must be set to the same as your 155 | # system time zone. 156 | TIME_ZONE = 'America/New_York' 157 | 158 | USE_I18N = True 159 | 160 | USE_L10N = True 161 | 162 | USE_TZ = True 163 | 164 | # Static files (CSS, JavaScript, Images) 165 | # https://docs.djangoproject.com/en/{{ docs_version }}/howto/static-files/ 166 | # Absolute path to the directory static files should be collected to. 167 | # Don't put anything in this directory yourself; store your static files 168 | # in apps' "static/" subdirectories and in STATICFILES_DIRS. 169 | # Example: "/home/media/media.lawrence.com/static/" 170 | STATIC_ROOT = os.path.join(PROJECT_ROOT, 'public', 'static') 171 | 172 | # URL prefix for static files. 173 | # Example: "http://media.lawrence.com/static/" 174 | STATIC_URL = '/static/' 175 | 176 | # Additional locations of static files 177 | STATICFILES_DIRS = ( 178 | os.path.join(BASE_DIR, 'static'), 179 | ) 180 | 181 | # If using Celery, tell it to obey our logging configuration. 182 | CELERYD_HIJACK_ROOT_LOGGER = False 183 | 184 | # https://docs.djangoproject.com/en/1.9/topics/auth/passwords/#password-validation 185 | AUTH_PASSWORD_VALIDATORS = [ 186 | { 187 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 188 | }, 189 | { 190 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 191 | }, 192 | { 193 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 194 | }, 195 | { 196 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 197 | }, 198 | ] 199 | 200 | # Make things more secure by default. Run "python manage.py check --deploy" 201 | # for even more suggestions that you might want to add to the settings, depending 202 | # on how the site uses SSL. 203 | SECURE_CONTENT_TYPE_NOSNIFF = True 204 | SECURE_BROWSER_XSS_FILTER = True 205 | CSRF_COOKIE_HTTPONLY = True 206 | X_FRAME_OPTIONS = 'DENY' 207 | -------------------------------------------------------------------------------- /project_name/settings/deploy.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | # Settings for live deployed environments: vagrant, staging, production, etc 4 | import os 5 | 6 | from .base import * # noqa 7 | 8 | os.environ.setdefault('CACHE_HOST', '127.0.0.1:11211') 9 | os.environ.setdefault('BROKER_HOST', '127.0.0.1:5672') 10 | 11 | #: deploy environment - e.g. "staging" or "production" 12 | ENVIRONMENT = os.environ['ENVIRONMENT'] 13 | 14 | 15 | DEBUG = False 16 | 17 | 18 | if 'MEDIA_ROOT' in os.environ: 19 | MEDIA_ROOT = os.getenv('MEDIA_ROOT') 20 | 21 | 22 | if 'DATABASE_URL' in os.environ: 23 | # Dokku 24 | SECRET_KEY = os.environ['DJANGO_SECRET_KEY'] 25 | 26 | import dj_database_url 27 | # Update database configuration with $DATABASE_URL. 28 | db_from_env = dj_database_url.config(conn_max_age=500) 29 | DATABASES['default'].update(db_from_env) 30 | 31 | # Disable Django's own staticfiles handling in favour of WhiteNoise, for 32 | # greater consistency between gunicorn and `./manage.py runserver`. See: 33 | # http://whitenoise.evans.io/en/stable/django.html#using-whitenoise-in-development 34 | INSTALLED_APPS.remove('django.contrib.staticfiles') 35 | INSTALLED_APPS.extend([ 36 | 'whitenoise.runserver_nostatic', 37 | 'django.contrib.staticfiles', 38 | ]) 39 | 40 | MIDDLEWARE.remove('django.middleware.security.SecurityMiddleware') 41 | MIDDLEWARE = [ 42 | 'django.middleware.security.SecurityMiddleware', 43 | 'whitenoise.middleware.WhiteNoiseMiddleware', 44 | ] + MIDDLEWARE 45 | 46 | # Allow all host headers (feel free to make this more specific) 47 | ALLOWED_HOSTS = ['*'] 48 | 49 | # Simplified static file serving. 50 | # https://warehouse.python.org/project/whitenoise/ 51 | STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' 52 | 53 | WEBSERVER_ROOT = os.path.join(PROJECT_ROOT, 'www') 54 | else: 55 | SECRET_KEY = os.environ['SECRET_KEY'] 56 | 57 | DATABASES['default']['NAME'] = os.environ.get('DB_NAME', '') 58 | DATABASES['default']['USER'] = os.environ.get('DB_USER', '') 59 | DATABASES['default']['HOST'] = os.environ.get('DB_HOST', '') 60 | DATABASES['default']['PORT'] = os.environ.get('DB_PORT', '') 61 | DATABASES['default']['PASSWORD'] = os.environ.get('DB_PASSWORD', '') 62 | 63 | WEBSERVER_ROOT = '/var/www/{{ project_name }}/' 64 | 65 | PUBLIC_ROOT = os.path.join(WEBSERVER_ROOT, 'public') 66 | 67 | STATIC_ROOT = os.path.join(PUBLIC_ROOT, 'static') 68 | 69 | MEDIA_ROOT = os.path.join(PUBLIC_ROOT, 'media') 70 | 71 | CACHES = { 72 | 'default': { 73 | 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 74 | 'LOCATION': '%(CACHE_HOST)s' % os.environ, 75 | } 76 | } 77 | 78 | EMAIL_HOST = os.environ.get('EMAIL_HOST', 'localhost') 79 | EMAIL_HOST_USER = os.environ.get('EMAIL_HOST_USER', '') 80 | EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_HOST_PASSWORD', '') 81 | EMAIL_USE_TLS = os.environ.get('EMAIL_USE_TLS', False) 82 | EMAIL_USE_SSL = os.environ.get('EMAIL_USE_SSL', False) 83 | # use TLS or SSL, not both: 84 | assert not (EMAIL_USE_TLS and EMAIL_USE_SSL) 85 | if EMAIL_USE_TLS: 86 | default_smtp_port = 587 87 | elif EMAIL_USE_SSL: 88 | default_smtp_port = 465 89 | else: 90 | default_smtp_port = 25 91 | EMAIL_PORT = os.environ.get('EMAIL_PORT', default_smtp_port) 92 | EMAIL_SUBJECT_PREFIX = '[{{ project_name|title }} %s] ' % ENVIRONMENT.title() 93 | DEFAULT_FROM_EMAIL = 'noreply@%(DOMAIN)s' % os.environ 94 | SERVER_EMAIL = DEFAULT_FROM_EMAIL 95 | 96 | CSRF_COOKIE_SECURE = True 97 | 98 | SESSION_COOKIE_SECURE = True 99 | 100 | SESSION_COOKIE_HTTPONLY = True 101 | 102 | ALLOWED_HOSTS = [os.environ['DOMAIN']] 103 | 104 | # Use template caching on deployed servers 105 | for backend in TEMPLATES: 106 | if backend['BACKEND'] == 'django.template.backends.django.DjangoTemplates': 107 | default_loaders = ['django.template.loaders.filesystem.Loader'] 108 | if backend.get('APP_DIRS', False): 109 | default_loaders.append('django.template.loaders.app_directories.Loader') 110 | # Django gets annoyed if you both set APP_DIRS True and specify your own loaders 111 | backend['APP_DIRS'] = False 112 | loaders = backend['OPTIONS'].get('loaders', default_loaders) 113 | for loader in loaders: 114 | if len(loader) == 2 and loader[0] == 'django.template.loaders.cached.Loader': 115 | # We're already caching our templates 116 | break 117 | else: 118 | backend['OPTIONS']['loaders'] = [('django.template.loaders.cached.Loader', loaders)] 119 | 120 | # Uncomment if using celery worker configuration 121 | # CELERY_SEND_TASK_ERROR_EMAILS = True 122 | # BROKER_URL = 'amqp://{{ project_name }}_%(ENVIRONMENT)s:%(BROKER_PASSWORD)s@%(BROKER_HOST)s/{{ project_name }}_%(ENVIRONMENT)s' % os.environ # noqa 123 | 124 | # Environment overrides 125 | # These should be kept to an absolute minimum 126 | if ENVIRONMENT.upper() == 'LOCAL': 127 | # Don't send emails from the Vagrant boxes 128 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 129 | 130 | 131 | if 'DOKKU_NGINX_SSL_PORT' in os.environ: 132 | # Dokku with SSL 133 | # SECURE_SSL_REDIRECT = True 134 | # Try HTTP Strict Transport Security (increase time if everything looks okay) 135 | # SECURE_HSTS_SECONDS = 1800 136 | # Honor the 'X-Forwarded-Proto' header for request.is_secure() 137 | SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') 138 | SECURE_REDIRECT_EXEMPT = ['/.well-known'] # For Let's Encrypt 139 | -------------------------------------------------------------------------------- /project_name/settings/dev.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | import os 3 | import sys 4 | 5 | from {{ project_name }}.settings.base import * # noqa 6 | 7 | DEBUG = True 8 | 9 | INSTALLED_APPS += ( 10 | 'debug_toolbar', 11 | ) 12 | MIDDLEWARE += ( 13 | 'debug_toolbar.middleware.DebugToolbarMiddleware', 14 | ) 15 | 16 | INTERNAL_IPS = ('127.0.0.1', ) 17 | 18 | #: Don't send emails, just print them on stdout 19 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 20 | 21 | #: Run celery tasks synchronously 22 | CELERY_ALWAYS_EAGER = True 23 | 24 | #: Tell us when a synchronous celery task fails 25 | CELERY_EAGER_PROPAGATES_EXCEPTIONS = True 26 | 27 | SECRET_KEY = os.environ.get('SECRET_KEY', '{{ secret_key }}') 28 | 29 | # Special test settings 30 | if 'test' in sys.argv: 31 | PASSWORD_HASHERS = ( 32 | 'django.contrib.auth.hashers.SHA1PasswordHasher', 33 | 'django.contrib.auth.hashers.MD5PasswordHasher', 34 | ) 35 | 36 | LOGGING['root']['handlers'] = [] 37 | -------------------------------------------------------------------------------- /project_name/settings/local.example.py: -------------------------------------------------------------------------------- 1 | from {{ project_name }}.settings.dev import * # noqa 2 | 3 | # Override settings here 4 | -------------------------------------------------------------------------------- /project_name/static/502.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Site Maintenance 6 | 28 | 29 | 30 |
31 |

Site Maintenance

32 |

We are down for some brief maintenance.

33 |

The site will be back to normal shortly.

34 |
35 | 36 | -------------------------------------------------------------------------------- /project_name/static/js/csrf_ajax.js: -------------------------------------------------------------------------------- 1 | import jQuery from 'jquery'; 2 | 3 | // Place any jQuery/helper plugins in here. 4 | (function ($) { 5 | 6 | // CSRF helper functions taken directly from Django docs 7 | function csrfSafeMethod(method) { 8 | // these HTTP methods do not require CSRF protection 9 | return (/^(GET|HEAD|OPTIONS|TRACE)$/i.test(method)); 10 | } 11 | 12 | function getCookie(name) { 13 | var cookieValue = null; 14 | if (document.cookie && document.cookie !== '') { 15 | var cookies = document.cookie.split(';'); 16 | for (var i = 0; i < cookies.length; i++) { 17 | var cookie = $.trim(cookies[i]); 18 | // Does this cookie string begin with the name we want? 19 | if (cookie.substring(0, name.length + 1) === (name + '=')) { 20 | cookieValue = decodeURIComponent( 21 | cookie.substring(name.length + 1)); 22 | break; 23 | } 24 | } 25 | } 26 | return cookieValue; 27 | } 28 | 29 | // Setup jQuery ajax calls to handle CSRF 30 | $.ajaxPrefilter(function (settings, originalOptions, xhr) { 31 | var csrftoken; 32 | if (!csrfSafeMethod(settings.type) && !this.crossDomain) { 33 | // Send the token to same-origin, relative URLs only. 34 | // Send the token only if the method warrants CSRF protection 35 | // Using the CSRFToken value acquired earlier 36 | csrftoken = getCookie('csrftoken'); 37 | xhr.setRequestHeader('X-CSRFToken', csrftoken); 38 | } 39 | }); 40 | 41 | })(jQuery); 42 | -------------------------------------------------------------------------------- /project_name/static/js/index.js: -------------------------------------------------------------------------------- 1 | import './csrf_ajax.js' 2 | import Modernizr from '../libs/modernizr.js' 3 | -------------------------------------------------------------------------------- /project_name/static/less/index.less: -------------------------------------------------------------------------------- 1 | /* Stylesheet Index. Import Less modules here. */ 2 | -------------------------------------------------------------------------------- /project_name/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-Agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /project_name/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block title %}Page Not Found :({% endblock %} 4 | 5 | {% block content %} 6 |

Not found :(

7 |

Sorry, but the page you were trying to view does not exist.

8 |

It looks like this was the result of either:

9 |
    10 |
  • a mistyped address
  • 11 |
  • an out-of-date link
  • 12 |
13 | {% endblock %} 14 | 15 | {% block extra-js %} 16 | 19 | 20 | {% endblock %} 21 | -------------------------------------------------------------------------------- /project_name/templates/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Server Error 6 | 28 | 29 | 30 |
31 |

Server Error :(

32 |

Something has gone horribly wrong.

33 |

We've notified our admins and they won't rest until it is fixed.

34 |
35 | 36 | -------------------------------------------------------------------------------- /project_name/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load static from staticfiles %} 2 | 3 | 4 | 5 | 6 | {% block title %}{% endblock %} 7 | 8 | 9 | 10 | 11 | {% block extra-meta %}{% endblock %} 12 | 13 | {% block extra-css %}{% endblock %} 14 | 15 | 16 | 17 |
18 | {% block content %}{% endblock %} 19 |
20 | 21 | {% block extra-js %}{% endblock %} 22 | 23 | 24 | -------------------------------------------------------------------------------- /project_name/urls.py: -------------------------------------------------------------------------------- 1 | """{{ project_name }} URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/{{ docs_version }}/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Add an import: from blog import urls as blog_urls 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) 15 | """ 16 | from django.conf import settings 17 | from django.conf.urls import url, include 18 | from django.conf.urls.static import static 19 | from django.contrib import admin 20 | from django.views.generic import TemplateView 21 | 22 | urlpatterns = [ 23 | url(r'^$', TemplateView.as_view(template_name='base.html')), 24 | url(r'^admin/', admin.site.urls), 25 | ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 26 | 27 | if settings.DEBUG: 28 | import debug_toolbar 29 | urlpatterns += [ 30 | url(r'^__debug__/', include(debug_toolbar.urls)), 31 | ] 32 | -------------------------------------------------------------------------------- /project_name/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for {{ project_name }} 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/{{ docs_version }}/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | from . import load_env 15 | 16 | load_env.load_env() 17 | if 'DATABASE_URL' in os.environ: 18 | # Dokku or similar 19 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings.deploy") 20 | else: 21 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "{{ project_name }}.settings") 22 | 23 | application = get_wsgi_application() 24 | 25 | try: 26 | from whitenoise.django import DjangoWhiteNoise 27 | except ImportError: 28 | pass 29 | else: 30 | application = DjangoWhiteNoise(application) 31 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Heroku and dokku look for requirements.txt in top directory when deploying 2 | -r requirements/production.txt 3 | -------------------------------------------------------------------------------- /requirements/base.txt: -------------------------------------------------------------------------------- 1 | # The comment on the next line tells requests.io to warn us if there's a newer 2 | # version of Django within the given range, but not for versions outside that 3 | # range. So if 2.2.99 gets released, we get warned. If 3.0.1 gets released, 4 | # we don't. 5 | Django==2.2.13 # rq.filter: >=2.2.7,<2.3 6 | # Required by Django 7 | sqlparse==0.3.0 8 | pytz 9 | 10 | dealer==2.0.5 11 | django-dotenv==1.4.2 12 | dj-database-url==0.5.0 13 | Pillow==6.2.0 14 | psycopg2-binary==2.8.3 15 | whitenoise==4.1.4 16 | -------------------------------------------------------------------------------- /requirements/dev.txt: -------------------------------------------------------------------------------- 1 | -r base.txt 2 | django-debug-toolbar==2.0 3 | 4 | coverage==4.5.4 5 | flake8==3.7.8 6 | # Required by flake8 7 | entrypoints==0.3 8 | pycodestyle==2.5.0 9 | pyflakes==2.1.1 10 | 11 | # For translation 12 | transifex-client==0.13.6 13 | 14 | requires.io 15 | 16 | # For docs 17 | Sphinx==1.7.2 18 | sphinx-rtd-theme==0.2.4 19 | alabaster==0.7.10 20 | Babel==2.5.3 21 | docutils==0.14 22 | Pygments==2.2.0 23 | snowballstemmer==1.2.1 24 | 25 | # For deployment 26 | Fabric3==1.14.post1 27 | asn1crypto==0.24.0 28 | bcrypt==3.1.7 29 | cffi==1.12.3 30 | cryptography==2.7 31 | paramiko==2.6.0 32 | pycparser==2.19 33 | pynacl==1.3.0 34 | -------------------------------------------------------------------------------- /requirements/production.txt: -------------------------------------------------------------------------------- 1 | -r base.txt 2 | python-memcached==1.59 3 | gunicorn==19.9.0 4 | newrelic 5 | -------------------------------------------------------------------------------- /runtime.txt: -------------------------------------------------------------------------------- 1 | python-3.7.0 2 | -------------------------------------------------------------------------------- /scripts/postdeploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | # This runs *inside* the container, very late in the deploy - I think, 5 | # after dokku has actually switched over to the newly deployed version 6 | # of the app. As such, it's too late for most of the things we might 7 | # want to do during a deploy. 8 | 9 | # Current directory is "/app", which is the root of the copy 10 | # of this repository inside the container. 11 | # Lots of env vars are set, and a bunch of shell vars too. 12 | # Uncomment the next few lines and try a deploy to see all the current ones. 13 | echo "POSTDEPLOY SCRIPT" 14 | #echo "Current directory: "$(pwd) 15 | #echo "LS:" 16 | #ls -A 17 | #echo "Environment:" 18 | #printenv|sort 19 | #echo "VARIABLES:" 20 | #set 21 | -------------------------------------------------------------------------------- /scripts/predeploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | # This runs *inside* the container, after things have been installed and 5 | # collectstatic has run. 6 | # Current directory is "/app", which is the root of the copy 7 | # of this repository inside the container. 8 | # Lots of env vars are set, and a bunch of shell vars too. 9 | # Uncomment the next few lines and try a deploy to see all the current ones. 10 | echo "PREDEPLOY SCRIPT" 11 | #echo "Current directory: "$(pwd) 12 | #echo "LS:" 13 | #ls -A 14 | #echo "Environment:" 15 | #printenv|sort 16 | #echo "VARIABLES:" 17 | #set 18 | #exit 1 19 | 20 | mkdir -p $MEDIA_ROOT 21 | 22 | # NOTE: the buildpack should already have installed our node packages 23 | npm run build 24 | # We need to run collectstatic here, even though the buildpack already did, 25 | # to pick up the output of the `npm run build` 26 | python manage.py collectstatic --noinput 27 | 28 | # Run migrations 29 | python manage.py migrate --noinput 30 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [flake8] 2 | max-line-length=100 3 | exclude=migrations,docs,node_modules 4 | --------------------------------------------------------------------------------