├── .gitignore ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md ├── bin └── entry-point.sh ├── conf └── docker │ ├── Dockerfile │ └── docker-compose.yml ├── project ├── manage.py └── project │ ├── __init__.py │ ├── apps │ ├── __init__.py │ └── user_login │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ └── __init__.py │ │ ├── models.py │ │ ├── tasks.py │ │ ├── tests.py │ │ └── views.py │ ├── celery_app.py │ ├── exceptions.py │ ├── settings │ ├── __init__.py │ ├── celery.py │ ├── common.py │ └── database.py │ ├── urls.py │ ├── views.py │ └── wsgi.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore intellij IDE things 2 | .idea/ 3 | 4 | i# ignore vsCode things 5 | settings.json 6 | 7 | # OsX DS_Store nonsense 8 | .DS_Store 9 | 10 | # ignore various Python things 11 | __pycache__/ 12 | *.py[cod] 13 | *$py.class 14 | *.so 15 | .Python 16 | env/ 17 | *.egg-info/ 18 | .installed.cfg 19 | *.egg 20 | *.manifest 21 | *.spec 22 | pip-log.txt 23 | pip-delete-this-directory.txt 24 | htmlcov/ 25 | .tox/ 26 | .coverage 27 | .coverage.* 28 | .cache 29 | nosetests.xml 30 | coverage.xml 31 | *,cover 32 | *.mo 33 | *.pot 34 | *.log 35 | docs/_build/ 36 | .docs/build 37 | target/ 38 | venv/ 39 | 40 | # ignore Google Drive API secrets 41 | *secrets.json 42 | *secrets.py 43 | 44 | # vscode 45 | .vscode/ 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 rm complexity 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | pylint = "*" 8 | "flake8" = "*" 9 | pylint-django = "*" 10 | django-allauth = "==0.36.0" 11 | "psycopg2" = "==2.7.4" 12 | pytz = ">dev" 13 | billiard = "<3.6.0,>=3.5.0.2" 14 | kombu = "==4.2.1" 15 | Django = "==1.11" 16 | ipdb = "==0.11" 17 | redis = ">=2.10.5" 18 | pydocstyle = "==1.1.1" 19 | cyanide = "*" 20 | flakeplus = "*" 21 | celery = "==4.2.1" 22 | 23 | [dev-packages] 24 | 25 | [requires] 26 | python_version = "3.6" 27 | -------------------------------------------------------------------------------- /Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "20de8fed2ea8d872c457ca6978c9833d6be12dad9f93393c11817949d5840112" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.6" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "amqp": { 20 | "hashes": [ 21 | "sha256:073dd02fdd73041bffc913b767866015147b61f2a9bc104daef172fc1a0066eb", 22 | "sha256:eed41946890cd43e8dee44a316b85cf6fee5a1a34bb4a562b660a358eb529e1b" 23 | ], 24 | "version": "==2.3.2" 25 | }, 26 | "appnope": { 27 | "hashes": [ 28 | "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", 29 | "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71" 30 | ], 31 | "markers": "sys_platform == 'darwin'", 32 | "version": "==0.1.0" 33 | }, 34 | "astroid": { 35 | "hashes": [ 36 | "sha256:292fa429e69d60e4161e7612cb7cc8fa3609e2e309f80c224d93a76d5e7b58be", 37 | "sha256:c7013d119ec95eb626f7a2011f0b63d0c9a095df9ad06d8507b37084eada1a8d" 38 | ], 39 | "version": "==2.0.4" 40 | }, 41 | "backcall": { 42 | "hashes": [ 43 | "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", 44 | "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2" 45 | ], 46 | "version": "==0.1.0" 47 | }, 48 | "billiard": { 49 | "hashes": [ 50 | "sha256:ed65448da5877b5558f19d2f7f11f8355ea76b3e63e1c0a6059f47cfae5f1c84" 51 | ], 52 | "index": "pypi", 53 | "version": "==3.5.0.4" 54 | }, 55 | "celery": { 56 | "hashes": [ 57 | "sha256:77dab4677e24dc654d42dfbdfed65fa760455b6bb563a0877ecc35f4cfcfc678", 58 | "sha256:ad7a7411772b80a4d6c64f2f7f723200e39fb66cf614a7fdfab76d345acc7b13" 59 | ], 60 | "index": "pypi", 61 | "version": "==4.2.1" 62 | }, 63 | "certifi": { 64 | "hashes": [ 65 | "sha256:339dc09518b07e2fa7eda5450740925974815557727d6bd35d319c1524a04a4c", 66 | "sha256:6d58c986d22b038c8c0df30d639f23a3e6d172a05c3583e766f4c0b785c0986a" 67 | ], 68 | "version": "==2018.10.15" 69 | }, 70 | "chardet": { 71 | "hashes": [ 72 | "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", 73 | "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" 74 | ], 75 | "version": "==3.0.4" 76 | }, 77 | "cyanide": { 78 | "hashes": [ 79 | "sha256:0f9e682dc493151b9c3b0aad417a8be98b29fddf311974e5a9470d00091eaa50", 80 | "sha256:5a75f962f313dd64bbe5b21983a76cf4def64cdfc249bfa1d92994c29bcc6326" 81 | ], 82 | "index": "pypi", 83 | "version": "==1.3.0" 84 | }, 85 | "decorator": { 86 | "hashes": [ 87 | "sha256:2c51dff8ef3c447388fe5e4453d24a2bf128d3a4c32af3fabef1f01c6851ab82", 88 | "sha256:c39efa13fbdeb4506c476c9b3babf6a718da943dab7811c206005a4a956c080c" 89 | ], 90 | "version": "==4.3.0" 91 | }, 92 | "defusedxml": { 93 | "hashes": [ 94 | "sha256:24d7f2f94f7f3cb6061acb215685e5125fbcdc40a857eff9de22518820b0a4f4", 95 | "sha256:702a91ade2968a82beb0db1e0766a6a273f33d4616a6ce8cde475d8e09853b20" 96 | ], 97 | "version": "==0.5.0" 98 | }, 99 | "django": { 100 | "hashes": [ 101 | "sha256:0120b3b60760fb0617848b58aaa9702c0bf963320ed472f0879c5c55ab75b64a", 102 | "sha256:b6f3b864944276b4fd1d099952112696558f78b77b39188ac92b6c5e80152c30" 103 | ], 104 | "index": "pypi", 105 | "version": "==1.11" 106 | }, 107 | "django-allauth": { 108 | "hashes": [ 109 | "sha256:7d9646e3560279d6294ebb4c361fef829708d106da697658cf158bf2ca57b474" 110 | ], 111 | "index": "pypi", 112 | "version": "==0.36.0" 113 | }, 114 | "flake8": { 115 | "hashes": [ 116 | "sha256:7253265f7abd8b313e3892944044a365e3f4ac3fcdcfb4298f55ee9ddf188ba0", 117 | "sha256:c7841163e2b576d435799169b78703ad6ac1bbb0f199994fc05f700b2a90ea37" 118 | ], 119 | "index": "pypi", 120 | "version": "==3.5.0" 121 | }, 122 | "flakeplus": { 123 | "hashes": [ 124 | "sha256:98375a58bf20b4bbf66b669621b72bc8e65ecef5b46c3aff4c09dc2cf5a20042", 125 | "sha256:9f0f27d88558aa935a3cb847a3c9a00365eb956e47b77585c00d6c6e067c823a" 126 | ], 127 | "index": "pypi", 128 | "version": "==1.1.0" 129 | }, 130 | "idna": { 131 | "hashes": [ 132 | "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", 133 | "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" 134 | ], 135 | "version": "==2.7" 136 | }, 137 | "ipdb": { 138 | "hashes": [ 139 | "sha256:7081c65ed7bfe7737f83fa4213ca8afd9617b42ff6b3f1daf9a3419839a2a00a" 140 | ], 141 | "index": "pypi", 142 | "version": "==0.11" 143 | }, 144 | "ipython": { 145 | "hashes": [ 146 | "sha256:47b17ea874454a5c2eacc2732b04a750d260b01ba479323155ac8a39031f5535", 147 | "sha256:9fed506c3772c875a3048bc134a25e6f5e997b1569b2636f6a5d891f34cbfd46" 148 | ], 149 | "version": "==7.0.1" 150 | }, 151 | "ipython-genutils": { 152 | "hashes": [ 153 | "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", 154 | "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" 155 | ], 156 | "version": "==0.2.0" 157 | }, 158 | "isort": { 159 | "hashes": [ 160 | "sha256:1153601da39a25b14ddc54955dbbacbb6b2d19135386699e2ad58517953b34af", 161 | "sha256:b9c40e9750f3d77e6e4d441d8b0266cf555e7cdabdcff33c4fd06366ca761ef8", 162 | "sha256:ec9ef8f4a9bc6f71eec99e1806bfa2de401650d996c59330782b89a5555c1497" 163 | ], 164 | "version": "==4.3.4" 165 | }, 166 | "jedi": { 167 | "hashes": [ 168 | "sha256:0191c447165f798e6a730285f2eee783fff81b0d3df261945ecb80983b5c3ca7", 169 | "sha256:b7493f73a2febe0dc33d51c99b474547f7f6c0b2c8fb2b21f453eef204c12148" 170 | ], 171 | "version": "==0.13.1" 172 | }, 173 | "kombu": { 174 | "hashes": [ 175 | "sha256:86adec6c60f63124e2082ea8481bbe4ebe04fde8ebed32c177c7f0cd2c1c9082", 176 | "sha256:b274db3a4eacc4789aeb24e1de3e460586db7c4fc8610f7adcc7a3a1709a60af" 177 | ], 178 | "index": "pypi", 179 | "version": "==4.2.1" 180 | }, 181 | "lazy-object-proxy": { 182 | "hashes": [ 183 | "sha256:0ce34342b419bd8f018e6666bfef729aec3edf62345a53b537a4dcc115746a33", 184 | "sha256:1b668120716eb7ee21d8a38815e5eb3bb8211117d9a90b0f8e21722c0758cc39", 185 | "sha256:209615b0fe4624d79e50220ce3310ca1a9445fd8e6d3572a896e7f9146bbf019", 186 | "sha256:27bf62cb2b1a2068d443ff7097ee33393f8483b570b475db8ebf7e1cba64f088", 187 | "sha256:27ea6fd1c02dcc78172a82fc37fcc0992a94e4cecf53cb6d73f11749825bd98b", 188 | "sha256:2c1b21b44ac9beb0fc848d3993924147ba45c4ebc24be19825e57aabbe74a99e", 189 | "sha256:2df72ab12046a3496a92476020a1a0abf78b2a7db9ff4dc2036b8dd980203ae6", 190 | "sha256:320ffd3de9699d3892048baee45ebfbbf9388a7d65d832d7e580243ade426d2b", 191 | "sha256:50e3b9a464d5d08cc5227413db0d1c4707b6172e4d4d915c1c70e4de0bbff1f5", 192 | "sha256:5276db7ff62bb7b52f77f1f51ed58850e315154249aceb42e7f4c611f0f847ff", 193 | "sha256:61a6cf00dcb1a7f0c773ed4acc509cb636af2d6337a08f362413c76b2b47a8dd", 194 | "sha256:6ae6c4cb59f199d8827c5a07546b2ab7e85d262acaccaacd49b62f53f7c456f7", 195 | "sha256:7661d401d60d8bf15bb5da39e4dd72f5d764c5aff5a86ef52a042506e3e970ff", 196 | "sha256:7bd527f36a605c914efca5d3d014170b2cb184723e423d26b1fb2fd9108e264d", 197 | "sha256:7cb54db3535c8686ea12e9535eb087d32421184eacc6939ef15ef50f83a5e7e2", 198 | "sha256:7f3a2d740291f7f2c111d86a1c4851b70fb000a6c8883a59660d95ad57b9df35", 199 | "sha256:81304b7d8e9c824d058087dcb89144842c8e0dea6d281c031f59f0acf66963d4", 200 | "sha256:933947e8b4fbe617a51528b09851685138b49d511af0b6c0da2539115d6d4514", 201 | "sha256:94223d7f060301b3a8c09c9b3bc3294b56b2188e7d8179c762a1cda72c979252", 202 | "sha256:ab3ca49afcb47058393b0122428358d2fbe0408cf99f1b58b295cfeb4ed39109", 203 | "sha256:bd6292f565ca46dee4e737ebcc20742e3b5be2b01556dafe169f6c65d088875f", 204 | "sha256:cb924aa3e4a3fb644d0c463cad5bc2572649a6a3f68a7f8e4fbe44aaa6d77e4c", 205 | "sha256:d0fc7a286feac9077ec52a927fc9fe8fe2fabab95426722be4c953c9a8bede92", 206 | "sha256:ddc34786490a6e4ec0a855d401034cbd1242ef186c20d79d2166d6a4bd449577", 207 | "sha256:e34b155e36fa9da7e1b7c738ed7767fc9491a62ec6af70fe9da4a057759edc2d", 208 | "sha256:e5b9e8f6bda48460b7b143c3821b21b452cb3a835e6bbd5dd33aa0c8d3f5137d", 209 | "sha256:e81ebf6c5ee9684be8f2c87563880f93eedd56dd2b6146d8a725b50b7e5adb0f", 210 | "sha256:eb91be369f945f10d3a49f5f9be8b3d0b93a4c2be8f8a5b83b0571b8123e0a7a", 211 | "sha256:f460d1ceb0e4a5dcb2a652db0904224f367c9b3c1470d5a7683c0480e582468b" 212 | ], 213 | "version": "==1.3.1" 214 | }, 215 | "mccabe": { 216 | "hashes": [ 217 | "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", 218 | "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" 219 | ], 220 | "version": "==0.6.1" 221 | }, 222 | "oauthlib": { 223 | "hashes": [ 224 | "sha256:ac35665a61c1685c56336bda97d5eefa246f1202618a1d6f34fccb1bdd404162", 225 | "sha256:d883b36b21a6ad813953803edfa563b1b579d79ca758fe950d1bc9e8b326025b" 226 | ], 227 | "version": "==2.1.0" 228 | }, 229 | "parso": { 230 | "hashes": [ 231 | "sha256:35704a43a3c113cce4de228ddb39aab374b8004f4f2407d070b6a2ca784ce8a2", 232 | "sha256:895c63e93b94ac1e1690f5fdd40b65f07c8171e3e53cbd7793b5b96c0e0a7f24" 233 | ], 234 | "version": "==0.3.1" 235 | }, 236 | "pexpect": { 237 | "hashes": [ 238 | "sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba", 239 | "sha256:3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b" 240 | ], 241 | "markers": "sys_platform != 'win32'", 242 | "version": "==4.6.0" 243 | }, 244 | "pickleshare": { 245 | "hashes": [ 246 | "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", 247 | "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" 248 | ], 249 | "version": "==0.7.5" 250 | }, 251 | "prompt-toolkit": { 252 | "hashes": [ 253 | "sha256:646b3401b3b0bb7752100bc9b7aeecb36cb09cdfc63652b5856708b5ba8db7da", 254 | "sha256:82766ffd7397e6661465e20bd1390db0781ca4fbbab4cf6c2578cacdd8b09754", 255 | "sha256:ccad8461b5d912782726af17122113e196085e7e11d57cf0c9b982bf1ab2c7be" 256 | ], 257 | "version": "==2.0.6" 258 | }, 259 | "psycopg2": { 260 | "hashes": [ 261 | "sha256:027ae518d0e3b8fff41990e598bc7774c3d08a3a20e9ecc0b59fb2aaaf152f7f", 262 | "sha256:092a80da1b052a181b6e6c765849c9b32d46c5dac3b81bf8c9b83e697f3cdbe8", 263 | "sha256:0b9851e798bae024ed1a2a6377a8dab4b8a128a56ed406f572f9f06194e4b275", 264 | "sha256:179c52eb870110a8c1b460c86d4f696d58510ea025602cd3f81453746fccb94f", 265 | "sha256:19983b77ec1fc2a210092aa0333ee48811fd9fb5f194c6cd5b927ed409aea5f8", 266 | "sha256:1d90379d01d0dc50ae9b40c863933d87ff82d51dd7d52cea5d1cb7019afd72cd", 267 | "sha256:27467fd5af1dcc0a82d72927113b8f92da8f44b2efbdb8906bd76face95b596d", 268 | "sha256:32702e3bd8bfe12b36226ba9846ed9e22336fc4bd710039d594b36bd432ae255", 269 | "sha256:33f9e1032095e1436fa9ec424abcbd4c170da934fb70e391c5d78275d0307c75", 270 | "sha256:36030ca7f4b4519ee4f52a74edc4ec73c75abfb6ea1d80ac7480953d1c0aa3c3", 271 | "sha256:363fbbf4189722fc46779be1fad2597e2c40b3f577dc618f353a46391cf5d235", 272 | "sha256:6f302c486132f8dd11f143e919e236ea4467d53bf18c451cac577e6988ecbd05", 273 | "sha256:733166464598c239323142c071fa4c9b91c14359176e5ae7e202db6bcc1d2eb5", 274 | "sha256:7cbc3b21ce2f681ca9ad2d8c0901090b23a30c955e980ebf1006d41f37068a95", 275 | "sha256:888bba7841116e529f407f15c6d28fe3ef0760df8c45257442ec2f14f161c871", 276 | "sha256:8966829cb0d21a08a3c5ac971a2eb67c3927ae27c247300a8476554cc0ce2ae8", 277 | "sha256:8bf51191d60f6987482ef0cfe8511bbf4877a5aa7f313d7b488b53189cf26209", 278 | "sha256:8eb94c0625c529215b53c08fb4e461546e2f3fc96a49c13d5474b5ad7aeab6cf", 279 | "sha256:8ebba5314c609a05c6955e5773c7e0e57b8dd817e4f751f30de729be58fa5e78", 280 | "sha256:932a4c101af007cb3132b1f8a9ffef23386acc53dad46536dc5ba43a3235ae02", 281 | "sha256:ad75fe10bea19ad2188c5cb5fc4cdf53ee808d9b44578c94a3cd1e9fc2beb656", 282 | "sha256:aeaba399254ca79c299d9fe6aa811d3c3eac61458dee10270de7f4e71c624998", 283 | "sha256:b178e0923c93393e16646155794521e063ec17b7cc9f943f15b7d4b39776ea2c", 284 | "sha256:b68e89bb086a9476fa85298caab43f92d0a6af135a5f433d1f6b6d82cafa7b55", 285 | "sha256:d74cf9234ba76426add5e123449be08993a9b13ff434c6efa3a07caa305a619f", 286 | "sha256:f3d3a88128f0c219bdc5b2d9ccd496517199660cea021c560a3252116df91cbd", 287 | "sha256:fe6a7f87356116f5ea840c65b032af17deef0e1a5c34013a2962dd6f99b860dd" 288 | ], 289 | "index": "pypi", 290 | "version": "==2.7.4" 291 | }, 292 | "ptyprocess": { 293 | "hashes": [ 294 | "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", 295 | "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f" 296 | ], 297 | "version": "==0.6.0" 298 | }, 299 | "pycodestyle": { 300 | "hashes": [ 301 | "sha256:682256a5b318149ca0d2a9185d365d8864a768a28db66a84a2ea946bcc426766", 302 | "sha256:6c4245ade1edfad79c3446fadfc96b0de2759662dc29d07d80a6f27ad1ca6ba9" 303 | ], 304 | "version": "==2.3.1" 305 | }, 306 | "pydocstyle": { 307 | "hashes": [ 308 | "sha256:20939fb9c2d61a9870449573d9ed762ec040c764b2241bec22c5ea956b9caf90", 309 | "sha256:f808d8fc23952fe93c2d85598732bfa854cb5ee8a25f8191f60600710f898e8d" 310 | ], 311 | "index": "pypi", 312 | "version": "==1.1.1" 313 | }, 314 | "pyflakes": { 315 | "hashes": [ 316 | "sha256:08bd6a50edf8cffa9fa09a463063c425ecaaf10d1eb0335a7e8b1401aef89e6f", 317 | "sha256:8d616a382f243dbf19b54743f280b80198be0bca3a5396f1d2e1fca6223e8805" 318 | ], 319 | "version": "==1.6.0" 320 | }, 321 | "pygments": { 322 | "hashes": [ 323 | "sha256:78f3f434bcc5d6ee09020f92ba487f95ba50f1e3ef83ae96b9d5ffa1bab25c5d", 324 | "sha256:dbae1046def0efb574852fab9e90209b23f556367b5a320c0bcb871c77c3e8cc" 325 | ], 326 | "version": "==2.2.0" 327 | }, 328 | "pylint": { 329 | "hashes": [ 330 | "sha256:1d6d3622c94b4887115fe5204982eee66fdd8a951cf98635ee5caee6ec98c3ec", 331 | "sha256:31142f764d2a7cd41df5196f9933b12b7ee55e73ef12204b648ad7e556c119fb" 332 | ], 333 | "index": "pypi", 334 | "version": "==2.1.1" 335 | }, 336 | "pylint-django": { 337 | "hashes": [ 338 | "sha256:5dc5f85caef2c5f9e61622b9cbd89d94edd3dcf546939b2974d18de4fa90d676", 339 | "sha256:bf313f10b68ed915a34f0f475cc9ff8c7f574a95302beb48b79c5993f7efd84c" 340 | ], 341 | "index": "pypi", 342 | "version": "==2.0.2" 343 | }, 344 | "pylint-plugin-utils": { 345 | "hashes": [ 346 | "sha256:8ad25a82bcce390d1d6b7c006c123e0cb18051839c9df7b8bdb7823c53fe676e" 347 | ], 348 | "version": "==0.4" 349 | }, 350 | "python-vagrant": { 351 | "hashes": [ 352 | "sha256:af9a8a9802d382d45dbea96aa3cfbe77c6e6ad65b3fe7b7c799d41ab988179c6" 353 | ], 354 | "version": "==0.5.15" 355 | }, 356 | "python3-openid": { 357 | "hashes": [ 358 | "sha256:0086da6b6ef3161cfe50fb1ee5cceaf2cda1700019fda03c2c5c440ca6abe4fa", 359 | "sha256:628d365d687e12da12d02c6691170f4451db28d6d68d050007e4a40065868502" 360 | ], 361 | "version": "==3.1.0" 362 | }, 363 | "pytz": { 364 | "hashes": [ 365 | "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053", 366 | "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277" 367 | ], 368 | "index": "pypi", 369 | "version": "==2018.5" 370 | }, 371 | "redis": { 372 | "hashes": [ 373 | "sha256:8a1900a9f2a0a44ecf6e8b5eb3e967a9909dfed219ad66df094f27f7d6f330fb", 374 | "sha256:a22ca993cea2962dbb588f9f30d0015ac4afcc45bee27d3978c0dbe9e97c6c0f" 375 | ], 376 | "index": "pypi", 377 | "version": "==2.10.6" 378 | }, 379 | "requests": { 380 | "hashes": [ 381 | "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", 382 | "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" 383 | ], 384 | "version": "==2.19.1" 385 | }, 386 | "requests-oauthlib": { 387 | "hashes": [ 388 | "sha256:8886bfec5ad7afb391ed5443b1f697c6f4ae98d0e5620839d8b4499c032ada3f", 389 | "sha256:e21232e2465808c0e892e0e4dbb8c2faafec16ac6dc067dd546e9b466f3deac8" 390 | ], 391 | "version": "==1.0.0" 392 | }, 393 | "simplegeneric": { 394 | "hashes": [ 395 | "sha256:dc972e06094b9af5b855b3df4a646395e43d1c9d0d39ed345b7393560d0b9173" 396 | ], 397 | "version": "==0.8.1" 398 | }, 399 | "six": { 400 | "hashes": [ 401 | "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", 402 | "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" 403 | ], 404 | "version": "==1.11.0" 405 | }, 406 | "traitlets": { 407 | "hashes": [ 408 | "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", 409 | "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9" 410 | ], 411 | "version": "==4.3.2" 412 | }, 413 | "typed-ast": { 414 | "hashes": [ 415 | "sha256:0948004fa228ae071054f5208840a1e88747a357ec1101c17217bfe99b299d58", 416 | "sha256:10703d3cec8dcd9eef5a630a04056bbc898abc19bac5691612acba7d1325b66d", 417 | "sha256:1f6c4bd0bdc0f14246fd41262df7dfc018d65bb05f6e16390b7ea26ca454a291", 418 | "sha256:25d8feefe27eb0303b73545416b13d108c6067b846b543738a25ff304824ed9a", 419 | "sha256:29464a177d56e4e055b5f7b629935af7f49c196be47528cc94e0a7bf83fbc2b9", 420 | "sha256:2e214b72168ea0275efd6c884b114ab42e316de3ffa125b267e732ed2abda892", 421 | "sha256:3e0d5e48e3a23e9a4d1a9f698e32a542a4a288c871d33ed8df1b092a40f3a0f9", 422 | "sha256:519425deca5c2b2bdac49f77b2c5625781abbaf9a809d727d3a5596b30bb4ded", 423 | "sha256:57fe287f0cdd9ceaf69e7b71a2e94a24b5d268b35df251a88fef5cc241bf73aa", 424 | "sha256:668d0cec391d9aed1c6a388b0d5b97cd22e6073eaa5fbaa6d2946603b4871efe", 425 | "sha256:68ba70684990f59497680ff90d18e756a47bf4863c604098f10de9716b2c0bdd", 426 | "sha256:6de012d2b166fe7a4cdf505eee3aaa12192f7ba365beeefaca4ec10e31241a85", 427 | "sha256:79b91ebe5a28d349b6d0d323023350133e927b4de5b651a8aa2db69c761420c6", 428 | "sha256:8550177fa5d4c1f09b5e5f524411c44633c80ec69b24e0e98906dd761941ca46", 429 | "sha256:898f818399cafcdb93cbbe15fc83a33d05f18e29fb498ddc09b0214cdfc7cd51", 430 | "sha256:94b091dc0f19291adcb279a108f5d38de2430411068b219f41b343c03b28fb1f", 431 | "sha256:a26863198902cda15ab4503991e8cf1ca874219e0118cbf07c126bce7c4db129", 432 | "sha256:a8034021801bc0440f2e027c354b4eafd95891b573e12ff0418dec385c76785c", 433 | "sha256:bc978ac17468fe868ee589c795d06777f75496b1ed576d308002c8a5756fb9ea", 434 | "sha256:c05b41bc1deade9f90ddc5d988fe506208019ebba9f2578c622516fd201f5863", 435 | "sha256:c9b060bd1e5a26ab6e8267fd46fc9e02b54eb15fffb16d112d4c7b1c12987559", 436 | "sha256:edb04bdd45bfd76c8292c4d9654568efaedf76fe78eb246dde69bdb13b2dad87", 437 | "sha256:f19f2a4f547505fe9072e15f6f4ae714af51b5a681a97f187971f50c283193b6" 438 | ], 439 | "markers": "python_version < '3.7' and implementation_name == 'cpython'", 440 | "version": "==1.1.0" 441 | }, 442 | "unipath": { 443 | "hashes": [ 444 | "sha256:09839adcc72e8a24d4f76d63656f30b5a1f721fc40c9bcd79d8c67bdd8b47dae", 445 | "sha256:e6257e508d8abbfb6ddd8ec357e33589f1f48b1599127f23b017124d90b0fff7" 446 | ], 447 | "version": "==1.1" 448 | }, 449 | "urllib3": { 450 | "hashes": [ 451 | "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", 452 | "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" 453 | ], 454 | "markers": "python_version != '3.2.*' and python_version != '3.1.*' and python_version >= '2.6' and python_version != '3.3.*' and python_version != '3.0.*' and python_version < '4'", 455 | "version": "==1.23" 456 | }, 457 | "vine": { 458 | "hashes": [ 459 | "sha256:52116d59bc45392af9fdd3b75ed98ae48a93e822cee21e5fda249105c59a7a72", 460 | "sha256:6849544be74ec3638e84d90bc1cf2e1e9224cc10d96cd4383ec3f69e9bce077b" 461 | ], 462 | "version": "==1.1.4" 463 | }, 464 | "wcwidth": { 465 | "hashes": [ 466 | "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", 467 | "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" 468 | ], 469 | "version": "==0.1.7" 470 | }, 471 | "wrapt": { 472 | "hashes": [ 473 | "sha256:d4d560d479f2c21e1b5443bbd15fe7ec4b37fe7e53d335d3b9b0a7b1226fe3c6" 474 | ], 475 | "version": "==1.10.11" 476 | } 477 | }, 478 | "develop": {} 479 | } 480 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # celery-workflow-example 2 | Example of a celery workflow. 3 | -------------------------------------------------------------------------------- /bin/entry-point.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/dumb-init /bin/sh 2 | python3.6 /srv/www/project/project/manage.py runserver 0.0.0.0:8000 2>&1 & 3 | celery -A project.celery_app:app --workdir=/srv/www/project/project worker --loglevel=debug 4 | 5 | -------------------------------------------------------------------------------- /conf/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6 2 | 3 | MAINTAINER Josue Balandrano Coronel 4 | 5 | EXPOSE 8000 6 | 7 | #upgrade 8 | RUN apt-get update 9 | 10 | RUN apt-get install -y vim dumb-init 11 | 12 | COPY requirements.txt /tmp/requirements.txt 13 | 14 | RUN pip install --upgrade pip && pip install -r /tmp/requirements.txt 15 | 16 | RUN useradd -m portal 17 | 18 | RUN mkdir -p /srv/www/project && chown -R portal /srv/www/project 19 | 20 | USER portal 21 | 22 | WORKDIR /srv/www/project 23 | -------------------------------------------------------------------------------- /conf/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: "3" 3 | services: 4 | redis: 5 | image: redis:4.0 6 | ports: 7 | - 6379:6379 8 | 9 | rabbitmq: 10 | image: rabbitmq:3.6.10-management 11 | volumes: 12 | - celery_django_test_amqp:/var/lib/rabbitmq/mnesia 13 | ports: 14 | - 5672:5672 15 | - 5673:5673 16 | - 15673:15673 17 | - 15672:15672 18 | environment: 19 | - RABBITMQ_DEFAULT_USER=user 20 | - RABBITMQ_DEFAULT_PASS=pwd 21 | - RABBITMQ_DEFAULT_VHOST=test 22 | 23 | postgres: 24 | image: postgres:10.3 25 | volumes: 26 | - celery_django_test_pg:/var/lib/postgresql/data 27 | ports: 28 | - 5432:5432 29 | environment: 30 | - POSTGRES_PASSWORD:pwd 31 | - POSTGRES_USER=user 32 | - POSTGRES_DB=db 33 | - PGDATA=/var/lib/postgresql/data/django_test 34 | 35 | django: 36 | build: 37 | context: ./../../. 38 | dockerfile: conf/docker/Dockerfile 39 | image: local/celery-example:python3.6 40 | links: 41 | - redis:redis 42 | - rabbitmq:rabbitmq 43 | - postgres:postgres 44 | volumes: 45 | - ./../../.:/srv/www/project 46 | ports: 47 | - 8000:8000 48 | dns: 49 | - 8.8.8.8 50 | - 8.8.4.4 51 | command: ./bin/entry-point.sh 52 | 53 | volumes: 54 | celery_django_test_amqp: 55 | celery_django_test_pg: 56 | -------------------------------------------------------------------------------- /project/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /project/project/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import, unicode_literals 2 | 3 | # This will make sure the app is always imported when 4 | # Django starts so that shared_task will use this app. 5 | from .celery_app import app as celery_app 6 | -------------------------------------------------------------------------------- /project/project/apps/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmcomplexity/celery-workflow-example/e72fb12d37d47e52d9ffc9a6e0788e0ae089c64d/project/project/apps/__init__.py -------------------------------------------------------------------------------- /project/project/apps/user_login/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmcomplexity/celery-workflow-example/e72fb12d37d47e52d9ffc9a6e0788e0ae089c64d/project/project/apps/user_login/__init__.py -------------------------------------------------------------------------------- /project/project/apps/user_login/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /project/project/apps/user_login/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UserLoginConfig(AppConfig): 5 | name = 'user_login' 6 | -------------------------------------------------------------------------------- /project/project/apps/user_login/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmcomplexity/celery-workflow-example/e72fb12d37d47e52d9ffc9a6e0788e0ae089c64d/project/project/apps/user_login/migrations/__init__.py -------------------------------------------------------------------------------- /project/project/apps/user_login/models.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: project.apps.user_login.models 3 | synposis: Model stuff 4 | """ 5 | 6 | 7 | # Create your models here. 8 | def user_login_data(username, data_id): 9 | """Return user login data. 10 | 11 | :param int data_id: Data id 12 | """ 13 | return { 14 | 'username': username, 15 | 'data': { 16 | 'id': data_id, 17 | 'device': 'device_name', 18 | 'is_new': True 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /project/project/apps/user_login/tasks.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: project.tasks 3 | synopsis: Celery tasks. 4 | """ 5 | import logging 6 | from celery import shared_task 7 | from project.apps.user_login.models import user_login_data 8 | 9 | 10 | LOGGER = logging.getLogger(__name__) 11 | 12 | 13 | @shared_task(bind=True) 14 | def check_device(self, username, login_data_id): 15 | """Check if device is new for user. 16 | 17 | :param str username: Username. 18 | :param int login_data_id: Data id. 19 | """ 20 | LOGGER.debug('check_device id: %s', self.request.id) 21 | login_data = user_login_data(username, login_data_id) 22 | return login_data 23 | 24 | 25 | @shared_task(bind=True) 26 | def notify_user(self, login_data, username): 27 | """Notify user. 28 | 29 | :param dict login_data: Login Data. 30 | :param dict username: Username. 31 | """ 32 | LOGGER.debug( 33 | "notify_user id: %s\n" 34 | "username: %s", 35 | self.request.id, 36 | username 37 | ) 38 | is_new = login_data.get('is_new') 39 | if is_new: 40 | LOGGER.debug("Notifying user.") 41 | return {"user_notified": is_new} 42 | 43 | 44 | @shared_task(bind=True) 45 | def save_event(self, login_data, username): 46 | """Save data. 47 | 48 | :param dict login_data: Login Data. 49 | :param dict username: Username. 50 | """ 51 | LOGGER.debug( 52 | "save_event id: %s \n" 53 | "username: %s", 54 | self.request.id, 55 | username 56 | ) 57 | LOGGER.debug("Saving data.: %s", login_data) 58 | return {"Data saved": True} 59 | 60 | 61 | @shared_task(bind=True) 62 | def post_to_api(self, data): 63 | """Post to external api. 64 | 65 | :param dict data: Data 66 | """ 67 | LOGGER.debug( 68 | "post_to_api id: %s\n" 69 | "data: %s", 70 | self.request.id, 71 | data 72 | ) 73 | _data = {} 74 | for response in data: 75 | _data = {**_data, **response} 76 | LOGGER.debug("Posting to eternal API: %s", _data) 77 | return { 78 | "response": 79 | { 80 | "data": "some data" 81 | } 82 | } 83 | 84 | 85 | @shared_task(bind=True) 86 | def process_response(self, data): 87 | """Process response. 88 | 89 | :param dict data: Data. 90 | """ 91 | LOGGER.debug( 92 | "process_response id: %s\n" 93 | "data: %s", 94 | self.request.id, 95 | data 96 | ) 97 | LOGGER.debug("Processing response.") 98 | return True 99 | -------------------------------------------------------------------------------- /project/project/apps/user_login/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /project/project/apps/user_login/views.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: project.apps.user_login.views 3 | synopsis: Views. 4 | """ 5 | # from django.shortcuts import render 6 | import logging 7 | from celery import group 8 | from django.http import HttpResponse 9 | from project.apps.user_login.tasks import ( 10 | check_device, 11 | notify_user, 12 | save_event, 13 | post_to_api, 14 | process_response 15 | ) 16 | 17 | 18 | LOGGER = logging.getLogger(__name__) 19 | 20 | 21 | # Create your views here. 22 | def process_login(request): 23 | """Process Login.""" 24 | res = ( 25 | check_device.s(username="username", login_data_id=123) | 26 | group( 27 | notify_user.s(username="username"), 28 | save_event.s(username="username") 29 | ) | 30 | post_to_api.s() | 31 | process_response.s() 32 | ).apply_async() 33 | return HttpResponse( 34 | f"" 35 | " " 36 | "

res: {res}

" 37 | " " 38 | "" 39 | ) 40 | -------------------------------------------------------------------------------- /project/project/celery_app.py: -------------------------------------------------------------------------------- 1 | """Celery setup""" 2 | from __future__ import absolute_import, unicode_literals 3 | import os 4 | import time 5 | from celery import Celery, group 6 | 7 | # set the default Django settings module for the 'celery' program. 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings') 9 | 10 | app = Celery('project') # pylint: disable=invalid-name 11 | 12 | # Using a string here means the worker don't have to serialize 13 | # the configuration object to child processes. 14 | # - namespace='CELERY' means all celery-related configuration keys 15 | # should have a `CELERY_` prefix. 16 | app.config_from_object('django.conf:settings', namespace='CELERY') 17 | 18 | # Load task modules from all registered Django app configs. 19 | app.autodiscover_tasks() 20 | 21 | 22 | @app.task(bind=True) 23 | def debug_task(self): 24 | """Debug Task""" 25 | print('Request: {0!r}'.format(self.request)) 26 | -------------------------------------------------------------------------------- /project/project/exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: project.exceptions 3 | synopsis: Custom Exceptions 4 | """ 5 | 6 | class TaskException(Exception): 7 | """Task Exception""" 8 | pass 9 | -------------------------------------------------------------------------------- /project/project/settings/__init__.py: -------------------------------------------------------------------------------- 1 | from project.settings.common import * 2 | from project.settings.database import * 3 | from project.settings.celery import * 4 | -------------------------------------------------------------------------------- /project/project/settings/celery.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: project.settings.celery 3 | synopsis: Celery settings 4 | """ 5 | CELERY_BROKER_URL = "amqp://user:pwd@rabbitmq:5672/test" 6 | CELERY_RESULT_BACKEND = "redis://redis:6379/0" 7 | CELERY_WORKER_LOG_FORMAT = ( 8 | "[CELERY] $(processName)s %(levelname)s %(asctime)s " 9 | "%(module)s '%(name)s.%(funcName)s:%(lineno)s: %(message)s" 10 | ) 11 | -------------------------------------------------------------------------------- /project/project/settings/common.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for project project. 3 | 4 | Generated by 'django-admin startproject' using Django 1.11. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.11/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/1.11/ref/settings/ 11 | """ 12 | 13 | import os 14 | from django.urls import reverse_lazy 15 | 16 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 17 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = '_CH@NG3_M3_!' 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = [] 30 | 31 | LANGUAGES = [ 32 | ('de', 'German'), 33 | ('en', 'English'), 34 | ] 35 | 36 | # Application definition 37 | 38 | INSTALLED_APPS = [ 39 | 'django.contrib.admin', 40 | 'django.contrib.auth', 41 | 'django.contrib.contenttypes', 42 | 'django.contrib.sessions', 43 | 'django.contrib.messages', 44 | 'django.contrib.staticfiles', 45 | 'django.contrib.sites', 46 | 'allauth.account', 47 | 'project.apps.user_login', 48 | ] 49 | 50 | MIDDLEWARE = [ 51 | 'django.middleware.security.SecurityMiddleware', 52 | 'django.contrib.sessions.middleware.SessionMiddleware', 53 | 'django.middleware.common.CommonMiddleware', 54 | 'django.middleware.csrf.CsrfViewMiddleware', 55 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 56 | 'django.contrib.messages.middleware.MessageMiddleware', 57 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 58 | ] 59 | 60 | ROOT_URLCONF = 'project.urls' 61 | 62 | TEMPLATES = [ 63 | { 64 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 65 | 'DIRS': [], 66 | 'APP_DIRS': True, 67 | 'OPTIONS': { 68 | 'context_processors': [ 69 | 'django.template.context_processors.debug', 70 | 'django.template.context_processors.request', 71 | 'django.contrib.auth.context_processors.auth', 72 | 'django.contrib.messages.context_processors.messages', 73 | ], 74 | }, 75 | }, 76 | ] 77 | 78 | WSGI_APPLICATION = 'project.wsgi.application' 79 | 80 | 81 | # Password validation 82 | # https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators 83 | 84 | AUTH_PASSWORD_VALIDATORS = [ 85 | { 86 | 'NAME': ( 87 | 'django.contrib.auth.password_validation.' 88 | 'UserAttributeSimilarityValidator' 89 | ), 90 | }, 91 | { 92 | 'NAME': ( 93 | 'django.contrib.auth.password_validation.' 94 | 'MinimumLengthValidator' 95 | ), 96 | }, 97 | { 98 | 'NAME': ( 99 | 'django.contrib.auth.password_validation.' 100 | 'CommonPasswordValidator' 101 | ), 102 | }, 103 | { 104 | 'NAME': ( 105 | 'django.contrib.auth.password_validation.' 106 | 'NumericPasswordValidator' 107 | ), 108 | }, 109 | ] 110 | 111 | 112 | # Internationalization 113 | # https://docs.djangoproject.com/en/1.11/topics/i18n/ 114 | 115 | LANGUAGE_CODE = 'en-us' 116 | 117 | TIME_ZONE = 'UTC' 118 | 119 | USE_I18N = True 120 | 121 | USE_L10N = True 122 | 123 | USE_TZ = True 124 | 125 | 126 | # Static files (CSS, JavaScript, Images) 127 | # https://docs.djangoproject.com/en/1.11/howto/static-files/ 128 | 129 | STATIC_URL = '/static/' 130 | 131 | FOO_SETTING = reverse_lazy('index-view') 132 | 133 | LOGGING = { 134 | 'version': 1, 135 | 'disable_existing_loggers': False, 136 | 'formatters': { 137 | 'verbose': { 138 | 'format': '[DJANGO] {levelname} {asctime} {module} ' 139 | '{name}.{funcName}:{lineno:d} {message}', 140 | 'style': '{', 141 | }, 142 | 'simple': { 143 | 'format': '{levelname}: {message}', 144 | 'style': '{', 145 | }, 146 | }, 147 | 'filters': { 148 | 'require_debug_false': { 149 | '()': 'django.utils.log.RequireDebugFalse', 150 | }, 151 | 'require_debug_true': { 152 | '()': 'django.utils.log.RequireDebugTrue', 153 | }, 154 | }, 155 | 'handlers': { 156 | 'console': { 157 | 'level': 'INFO', 158 | 'class': 'logging.StreamHandler', 159 | 'formatter': 'simple', 160 | 'filters': ['require_debug_false'], 161 | }, 162 | 'console_debug': { 163 | 'level': 'DEBUG', 164 | 'class': 'logging.StreamHandler', 165 | 'formatter': 'verbose', 166 | 'filters': ['require_debug_true'], 167 | } 168 | }, 169 | 'loggers': { 170 | 'django': { 171 | 'handlers': ['console'], 172 | 'propagate': True, 173 | }, 174 | 'project': { 175 | 'handlers': ['console', 'console_debug'], 176 | 'level': 'DEBUG', 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /project/project/settings/database.py: -------------------------------------------------------------------------------- 1 | """ 2 | .. module:: project.settings.database 3 | synopsis: Database config 4 | """ 5 | DATABASES = { 6 | 'default': { 7 | 'ENGINE': 'django.db.backends.postgresql', 8 | 'NAME': 'db', 9 | 'USER': 'user', 10 | 'PASSWORD': 'pwd', 11 | 'HOST': 'postgres', 12 | 'PORT': '5432', 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /project/project/urls.py: -------------------------------------------------------------------------------- 1 | """project URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.11/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. Import the include() function: from django.conf.urls import url, include 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import url 17 | from django.contrib import admin 18 | from project.views import current_datetime 19 | 20 | urlpatterns = [ 21 | url(r'^$', current_datetime, name='index-view'), 22 | url(r'^admin/', admin.site.urls, name='admin-views'), 23 | ] 24 | -------------------------------------------------------------------------------- /project/project/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.http import HttpResponse 3 | import datetime 4 | 5 | def current_datetime(request): 6 | now = datetime.datetime.now() 7 | html = "It is now %s." % now 8 | return HttpResponse(html) 9 | -------------------------------------------------------------------------------- /project/project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for project 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/1.11/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django==1.11 2 | django-allauth==0.36.0 3 | psycopg2==2.7.4 4 | pytz>dev 5 | billiard>=3.5.0.2,<3.6.0 6 | kombu==4.2.1 7 | ipdb==0.11 8 | redis>=2.10.5 9 | celery==4.2.1 10 | --------------------------------------------------------------------------------