diff --git a/.circleci/config.yml b/.circleci/config.yml index 65726d8c..a3f618c2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,9 +1,5 @@ version: 2.1 -orbs: - aws-ecr: circleci/aws-ecr@4.0.1 - aws-ecs: circleci/aws-ecs@0.0.8 - workflows: build_test_deploy: jobs: @@ -19,7 +15,6 @@ workflows: - master - staging - jobs: build_test: docker: @@ -46,8 +41,6 @@ jobs: command: | sudo pip install poetry poetry install - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > /usr/local/bin/cc-test-reporter - chmod +x /usr/local/bin/cc-test-reporter - run: name: Initializing CodeClimate @@ -103,10 +96,17 @@ jobs: echo ${DOCKER_HUB_PASSWORD} | docker login -u ${DOCKER_HUB_USERNAME} --password-stdin if [[ "$CIRCLE_BRANCH" == "master" ]]; then - docker build -t operationcode/back-end:${CIRCLE_BRANCH}${CIRCLE_WORKFLOW_ID} -t operationcode/back-end:latest -f docker/Dockerfile . - docker push operationcode/back-end:latest + docker build -f docker/Dockerfile --target Prod \ + -t operationcode/back-end:${CIRCLE_BRANCH}-${CIRCLE_WORKFLOW_ID} \ + -t operationcode/back-end:master \ + -t operationcode/back-end:latest . else - docker build -t operationcode/back-end:${CIRCLE_BRANCH}${CIRCLE_WORKFLOW_ID} -f docker/Dockerfile --target Staging . + docker build -f docker/Dockerfile --target Staging \ + -t operationcode/back-end:${CIRCLE_BRANCH}-${CIRCLE_WORKFLOW_ID} \ + -t operationcode/back-end:staging \ + -t operationcode/back-end:latest . fi - docker push operationcode/back-end:${CIRCLE_BRANCH}${CIRCLE_WORKFLOW_ID} \ No newline at end of file + docker push operationcode/back-end:latest + docker push operationcode/back-end:${CIRCLE_BRANCH} + docker push operationcode/back-end:${CIRCLE_BRANCH}-${CIRCLE_WORKFLOW_ID} diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..8f436477 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +custom: https://secure.lglforms.com/form_engine/s/BRtP7QUKyHOyEYsZROsRew diff --git a/README.md b/README.md index 6d377b82..4685826f 100644 --- a/README.md +++ b/README.md @@ -22,19 +22,17 @@ # Welcome! - -This is a work in progress rewrite of the current [OperationCode](https://operationcode.org) backend. +This is the back-end application for [OperationCode](https://operationcode.org). We highly recommend [joining our organization](https://operationcode.org/join) to receive an invite to our Slack team. From there, you'll want to join the `#oc-python-projects` and `#oc-projects` channels. You can get help from multiple professional developers, including people who have worked on the application since day 1! -Our website is currently served by code located [here](https://github.com/OperationCode/operationcode_backend), -but that repository is no longer being actively developed. + +Before contributing, please review our [Contributing Guide](CONTRIBUTING.md) ## Maintainers For information about the maintainers of the project, check out [MAINTAINERS.md](MAINTAINERS.md). ## Quick Start - Recommended versions of tools used within the repo: - `python@3.7` or greater (in some environments, you may need to specify version of python i.e. `python test.py` vs `python3 test.py`)) - `git@2.17.1` or greater diff --git a/docker/Dockerfile b/docker/Dockerfile index 9e94542d..686025a9 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -39,8 +39,10 @@ ENV PIP_NO_BINARY psycopg2 # copy installed deps from builder image COPY --from=builder /opt/venv /opt/venv -# Add libpq which psycopg2 needs at runtime + +# Add libpq and libjpeg which psycopg2 and pillow need at runtime RUN apk add --no-cache libpq libjpeg-turbo + # Make sure we use the virtualenv ENV PATH="/opt/venv/bin:$PATH" @@ -55,19 +57,6 @@ COPY src /src WORKDIR /src -# The `dev-base` stage is used as the base for images that require -# the development dependencies. The duplication of the COPY instruction -# avoids breaking the cache for that later when the poetry.lock changes -FROM built-image AS dev-base - -ENV DJANGO_ENV development -COPY poetry.lock pyproject.toml ./ -RUN pip install poetry \ - && poetry config settings.virtualenvs.create false \ - && poetry install --no-interaction --no-ansi -COPY . / - - # `Shell` will build an image that, when run, drops you into a # python shell with the application context loaded # Note this stage name is capitalised, this is purely @@ -78,34 +67,45 @@ LABEL shell=true CMD python manage.py shell -# `Dev` runs the application using the development web server, and enables -# developer tools like the debugger and interactive expcetions -FROM dev-base AS Dev -LABEL dev=true -EXPOSE 8000 -WORKDIR /src - -CMD python manage.py collectstatic --no-input;\ - gunicorn operationcode_backend.wsgi -b 0.0.0.0:8000 -w 3 --access-logfile=- --error-logfile=- --capture-output --logger-class "operationcode_backend.custom_logging.CustomGunicornLogger" - # The `Staging` stage is the same configuration as prod, the only # difference being it doesn't process background tasks # The resulting image will run the application using a -# production webserver and configuration +# production webserver and the `environments/staging.py` configuration FROM app As Staging ENV DJANGO_ENV staging EXPOSE 8000 -CMD python manage.py collectstatic --no-input;\ - gunicorn operationcode_backend.wsgi -b 0.0.0.0:8000 -w 3 --access-logfile=- --error-logfile=- --capture-output --logger-class "operationcode_backend.custom_logging.CustomGunicornLogger" +CMD gunicorn operationcode_backend.wsgi -b 0.0.0.0:8000 -w 3 --access-logfile=- --error-logfile=- --capture-output --logger-class "operationcode_backend.custom_logging.CustomGunicornLogger" -# The `Prod` stage is the default stage if the Dockerfile is run without -# a target stage set. The resulting image will run the application using a -# production webserver and configuration +# The `Prod` stage creates an image that will run the application using a +# production webserver and the `environments/production.py` configuration FROM app As Prod ENV DJANGO_ENV production EXPOSE 8000 +CMD python manage.py process_tasks &\ + gunicorn operationcode_backend.wsgi -b 0.0.0.0:8000 -w 3 --access-logfile=- --error-logfile=- --capture-output --logger-class "operationcode_backend.custom_logging.CustomGunicornLogger" + + +# The `dev-base` stage is used as the base for images that require +# the development dependencies. The duplication of the COPY instruction +# avoids breaking the cache for that later when the poetry.lock changes +FROM built-image AS dev-base + +ENV DJANGO_ENV development +COPY poetry.lock pyproject.toml ./ +RUN pip install poetry \ + && poetry config settings.virtualenvs.create false \ + && poetry install --no-interaction --no-ansi +COPY . / + +# The `Dev` stage is the default stage if the Dockerfile is run without a target stage set. +# It runs the application using the development web server, and enables +# developer tools like the debugger and interactive expcetions +FROM dev-base AS Dev +LABEL dev=true +EXPOSE 8000 +WORKDIR /src + CMD python manage.py collectstatic --no-input;\ - python manage.py process_tasks &\ gunicorn operationcode_backend.wsgi -b 0.0.0.0:8000 -w 3 --access-logfile=- --error-logfile=- --capture-output --logger-class "operationcode_backend.custom_logging.CustomGunicornLogger" diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 9eede286..ca22e763 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -6,17 +6,12 @@ services: build: context: .. dockerfile: docker/Dockerfile -# target: Dev + target: Staging container_name: backend02 env_file: - .env -# volumes: -# - ../src:/src ports: - 8000:8000 - # depends_on: - # - db - pybot: image: pybot:latest @@ -24,27 +19,4 @@ services: env_file: - pybot.env ports: - - 5000:5000 - - - - ngrok-pyback: - image: wernight/ngrok:latest - env_file: - - .ngrok.env - environment: - - NGROK_PORT=backend:8000 - - NGROK_SUBDOMAIN=pyback - ports: - - 4040:4040 - -# db: -# image: postgres:10.1-alpine -# container_name: pg01 -# ports: -# - 5434:5432 -# volumes: -# - postgres_data:/var/lib/postgresql/data/ - -#volumes: -# postgres_data: \ No newline at end of file + - 5000:5000 \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 0f585eb2..654985e8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -69,10 +69,10 @@ description = "The AWS SDK for Python" name = "boto3" optional = false python-versions = "*" -version = "1.9.163" +version = "1.9.164" [package.dependencies] -botocore = ">=1.12.163,<1.13.0" +botocore = ">=1.12.164,<1.13.0" jmespath = ">=0.7.1,<1.0.0" s3transfer = ">=0.2.0,<0.3.0" @@ -82,7 +82,7 @@ description = "Low-level, data-driven core of boto 3." name = "botocore" optional = false python-versions = "*" -version = "1.12.163" +version = "1.12.164" [package.dependencies] docutils = ">=0.10" @@ -266,6 +266,17 @@ version = "1.11" Django = ">=1.11" sqlparse = ">=0.2.0" +[[package]] +category = "main" +description = "Django middlewares to monitor your application with Prometheus.io." +name = "django-prometheus" +optional = false +python-versions = "*" +version = "1.0.15" + +[package.dependencies] +prometheus-client = ">=0.0.21" + [[package]] category = "main" description = "Django reCaptcha v2 field/widget" @@ -323,7 +334,7 @@ description = "Tweak the form field rendering in templates, not in python-level name = "django-widget-tweaks" optional = false python-versions = "*" -version = "1.4.3" +version = "1.4.5" [[package]] category = "main" @@ -631,6 +642,14 @@ version = "0.12.0" [package.dependencies] importlib-metadata = ">=0.12" +[[package]] +category = "main" +description = "Python client for the Prometheus monitoring system." +name = "prometheus-client" +optional = false +python-versions = "*" +version = "0.7.0" + [[package]] category = "main" description = "psycopg2 - Python-PostgreSQL Database Adapter" @@ -814,7 +833,7 @@ description = "YAML parser and emitter for Python" name = "pyyaml" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -version = "5.1" +version = "5.1.1" [[package]] category = "main" @@ -848,7 +867,7 @@ description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip pres name = "ruamel.yaml" optional = false python-versions = "*" -version = "0.15.96" +version = "0.15.97" [[package]] category = "main" @@ -856,7 +875,7 @@ description = "An Amazon S3 Transfer Manager" name = "s3transfer" optional = false python-versions = "*" -version = "0.2.0" +version = "0.2.1" [package.dependencies] botocore = ">=1.12.36,<2.0.0" @@ -958,7 +977,7 @@ python-versions = ">=2.7" version = "0.5.1" [metadata] -content-hash = "b90cdf7fea572ba6cb0d876c71f483a361a5160f7970e5827924437320cf7876" +content-hash = "1ed386d822fbda222bfce73223fcecd3a1419b2fa3d3c17fb0e57e3f8c80d773" python-versions = "^3.7" [metadata.hashes] @@ -968,8 +987,8 @@ attrs = ["69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", "f0 bandit = ["d31a7b0819fe95d591106ba2d6c35568a513aba24db537ca71984781312a8e95", "e50fb4ed4ee8a98b8329385e48e606fded0999a2cb3e2acb6e7213c962ff0de1"] bcrypt = ["0ba875eb67b011add6d8c5b76afbd92166e98b1f1efab9433d5dc0fafc76e203", "21ed446054c93e209434148ef0b362432bb82bbdaf7beef70a32c221f3e33d1c", "28a0459381a8021f57230954b9e9a65bb5e3d569d2c253c5cac6cb181d71cf23", "2aed3091eb6f51c26b7c2fad08d6620d1c35839e7a362f706015b41bd991125e", "2fa5d1e438958ea90eaedbf8082c2ceb1a684b4f6c75a3800c6ec1e18ebef96f", "3a73f45484e9874252002793518da060fb11eaa76c30713faa12115db17d1430", "3e489787638a36bb466cd66780e15715494b6d6905ffdbaede94440d6d8e7dba", "44636759d222baa62806bbceb20e96f75a015a6381690d1bc2eda91c01ec02ea", "678c21b2fecaa72a1eded0cf12351b153615520637efcadc09ecf81b871f1596", "75460c2c3786977ea9768d6c9d8957ba31b5fbeb0aae67a5c0e96aab4155f18c", "8ac06fb3e6aacb0a95b56eba735c0b64df49651c6ceb1ad1cf01ba75070d567f", "8fdced50a8b646fff8fa0e4b1c5fd940ecc844b43d1da5a980cb07f2d1b1132f", "9b2c5b640a2da533b0ab5f148d87fb9989bf9bcb2e61eea6a729102a6d36aef9", "a9083e7fa9adb1a4de5ac15f9097eb15b04e2c8f97618f1b881af40abce382e1", "b7e3948b8b1a81c5a99d41da5fb2dc03ddb93b5f96fcd3fd27e643f91efa33e1", "b998b8ca979d906085f6a5d84f7b5459e5e94a13fc27c28a3514437013b6c2f6", "dd08c50bc6f7be69cd7ba0769acca28c846ec46b7a8ddc2acf4b9ac6f8a7457e", "de5badee458544ab8125e63e39afeedfcf3aef6a6e2282ac159c95ae7472d773", "ede2a87333d24f55a4a7338a6ccdccf3eaa9bed081d1737e0db4dbd1a4f7e6b6"] black = ["09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf", "68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"] -boto3 = ["ce59df424c1a711edaf3a93e25c8ac58fdbf188d8e7ab3cf6cf25a79454f6068", "d792fe90df487253d043dfbf1cfc04f28eb3c4aed0bed3643edcba338e3221d2"] -botocore = ["313bb98295e8369ca476faa553b19efdfcccf3d832691db3131fc6b842592af1", "a568bcfdd404a034633b86b166bf9118c5e1765c3e83f2188063bc73b58c0e89"] +boto3 = ["4871a758e8a91da45c89db266817fb236c2b935c620e0e6b2d1284c16e39ce1a", "8bf9159f6703b4c081d67ff0192ca47bdd99daf96848721b265f3e2450e0b49a"] +botocore = ["0c36d4b9bff79f1338e204de5fa1e2cd9f8c30fbc3b8ca35e5bfec7e88af7bad", "19eaccb6704996ed86482c2d88a2871322e98fa3ad6c7db19e12f9cfb516a518"] certifi = ["59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", "b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"] cffi = ["041c81822e9f84b1d9c401182e174996f0bae9991f33725d059b771744290774", "046ef9a22f5d3eed06334d01b1e836977eeef500d9b78e9ef693f9380ad0b83d", "066bc4c7895c91812eff46f4b1c285220947d4aa46fa0a2651ff85f2afae9c90", "066c7ff148ae33040c01058662d6752fd73fbc8e64787229ea8498c7d7f4041b", "2444d0c61f03dcd26dbf7600cf64354376ee579acad77aef459e34efcb438c63", "300832850b8f7967e278870c5d51e3819b9aad8f0a2c8dbe39ab11f119237f45", "34c77afe85b6b9e967bd8154e3855e847b70ca42043db6ad17f26899a3df1b25", "46de5fa00f7ac09f020729148ff632819649b3e05a007d286242c4882f7b1dc3", "4aa8ee7ba27c472d429b980c51e714a24f47ca296d53f4d7868075b175866f4b", "4d0004eb4351e35ed950c14c11e734182591465a33e960a4ab5e8d4f04d72647", "4e3d3f31a1e202b0f5a35ba3bc4eb41e2fc2b11c1eff38b362de710bcffb5016", "50bec6d35e6b1aaeb17f7c4e2b9374ebf95a8975d57863546fa83e8d31bdb8c4", "55cad9a6df1e2a1d62063f79d0881a414a906a6962bc160ac968cc03ed3efcfb", "5662ad4e4e84f1eaa8efce5da695c5d2e229c563f9d5ce5b0113f71321bcf753", "59b4dc008f98fc6ee2bb4fd7fc786a8d70000d058c2bbe2698275bc53a8d3fa7", "73e1ffefe05e4ccd7bcea61af76f36077b914f92b76f95ccf00b0c1b9186f3f9", "a1f0fd46eba2d71ce1589f7e50a9e2ffaeb739fb2c11e8192aa2b45d5f6cc41f", "a2e85dc204556657661051ff4bab75a84e968669765c8a2cd425918699c3d0e8", "a5457d47dfff24882a21492e5815f891c0ca35fefae8aa742c6c263dac16ef1f", "a8dccd61d52a8dae4a825cdbb7735da530179fea472903eb871a5513b5abbfdc", "ae61af521ed676cf16ae94f30fe202781a38d7178b6b4ab622e4eec8cefaff42", "b012a5edb48288f77a63dba0840c92d0504aa215612da4541b7b42d849bc83a3", "d2c5cfa536227f57f97c92ac30c8109688ace8fa4ac086d19d0af47d134e2909", "d42b5796e20aacc9d15e66befb7a345454eef794fdb0737d1af593447c6c8f45", "dee54f5d30d775f525894d67b1495625dd9322945e7fee00731952e0368ff42d", "e070535507bd6aa07124258171be2ee8dfc19119c28ca94c9dfb7efd23564512", "e1ff2748c84d97b065cc95429814cdba39bcbd77c9c85c89344b317dc0d9cbff", "ed851c75d1e0e043cbf5ca9a8e1b13c4c90f3fbd863dacb01c0808e2b5204201"] chardet = ["84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"] @@ -986,12 +1005,13 @@ django-background-tasks = ["35a9a54961f3e4486ab2f9482d1e8ac63ab4f47e5e0b7e654a22 django-compat = ["3ac9a3bedc56b9365d9eb241bc5157d0c193769bf995f9a78dc1bc24e7c2331b"] django-cors-headers = ["5b80bf0f8d7fc6e2bcb4f40781d5ff3661961bbf1982e52daec77241dea3b890", "ebf3e2cf25aa6993b959a8e6a87828ebb3c8fe5bc3ec4a2d6e65f3b8d9b4212c"] django-debug-toolbar = ["89d75b60c65db363fb24688d977e5fbf0e73386c67acf562d278402a10fc3736", "c2b0134119a624f4ac9398b44f8e28a01c7686ac350a12a74793f3dd57a9eea0"] +django-prometheus = ["571d89a13e2547e1c3d19901f155bde8e101323fbcb0439363d670bc61c8c541", "e8da2eb91cb20cfe0b8130305d34f3503766b7a08a244ce1031c36136beeb7a5"] django-recaptcha2 = ["9ea90db0cec502741be1066c09ec1b8e02a73162a319a042e78e67c4605087af", "c0b43851b05c6bf6ebb5ecc890c13ccedacd9bb33d64b4291c74dd6fcbc89366"] django-rest-auth = ["f11e12175dafeed772f50d740d22caeab27e99a3caca24ec65e66a8d6de16571"] django-split-settings = ["04f6b551111fdbcb4f669ac9004ac2d4a27197a36ca88444cb896378807ed777", "14b164dbd724e52aa9a0b0338eefa988a8a0f17f7cba34e0f983e5348682aeb7"] django-storages = ["8e35d2c7baeda5dc6f0b4f9a0fc142d25f9a1bf72b8cebfcbc5db4863abc552d", "b1a63cd5ea286ee5a9fb45de6c3c5c0ae132d58308d06f1ce9865cfcd5e470a7"] django-suit = ["19ed865a478dfca81cb5f50a70317700dd70da92c465093251d0e14330a2b92b"] -django-widget-tweaks = ["a69cba6c8a6b98f0cf6eef0535f8212d635e19044ee4533d4d78df700c2e233f", "bc645ef88307bc4ac269ee8ee9e572be814cd4a125c2bb6edb59ffcdc194982d"] +django-widget-tweaks = ["65c960f3d75008a285e4b10f4d21f9eae4160fd77a0f6097ad545185f8648bd6", "f2e2c9c9be1ccc59061e248dcc2144f4906d594abe1a563902f4bdf6aa14e432"] djangorestframework = ["376f4b50340a46c15ae15ddd0c853085f4e66058f97e4dbe7d43ed62f5e60651", "c12869cfd83c33d579b17b3cb28a2ae7322a53c3ce85580c2a2ebe4e3f56c4fb"] djangorestframework-camel-case = ["4bb2e41fb8a5d3745e20c5ee0842ebc6f6bac602b3286c3dd913b01760a2abb0", "5b957f9cf16730f153a0ab4add9ff17fb41b7fceaa7edad29b0536b515bffd16"] djangorestframework-jwt = ["5efe33032f3a4518a300dc51a51c92145ad95fb6f4b272e5aa24701db67936a7", "ab15dfbbe535eede8e2e53adaf52ef0cf018ee27dbfad10cbc4cbec2ab63d38c"] @@ -1023,6 +1043,7 @@ packaging = ["0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af", pbr = ["0ce920b865091450bbcd452b35cf6d6eb8a6d9ce13ad2210d6e77557f85cf32b", "93d2dc6ee0c9af4dbc70bc1251d0e545a9910ca8863774761f92716dece400b6"] pillow = ["0683e80d81e840d401b687ebc00a02bbb23d0793c34d0852a5af64cfa1589540", "09c4e81c3277199898e8dc2d85d94febad87c41251ecbd447ba7d64d94765bd8", "0ee74a23022af9baf997e3016b4e090e4ff08688d37a6f49010338ab46cfe101", "10860baedfe5da7c43cd17835b091494dcc59dda5ad176a011713fe398ea6ac2", "15c056bfa284c30a7f265a41ac4cbbc93bdbfc0dfe0613b9cb8a8581b51a9e55", "1a4e06ba4f74494ea0c58c24de2bb752818e9d504474ec95b0aa94f6b0a7e479", "1c3c707c76be43c9e99cb7e3d5f1bee1c8e5be8b8a2a5eeee665efbf8ddde91a", "1fd0b290203e3b0882d9605d807b03c0f47e3440f97824586c173eca0aadd99d", "24114e4a6e1870c5a24b1da8f60d0ba77a0b4027907860188ea82bd3508c80eb", "258d886a49b6b058cd7abb0ab4b2b85ce78669a857398e83e8b8e28b317b5abb", "2734c55f7d054b0ad889c971136cbb0a5b35a921e27beaa44fdc2436af529c6e", "2ac36ec56727a95bd5a04dfca6abce1db8042c31ee73b65796a42f31fd52d009", "2bc1002b573d107c0b172a5da0f34b4900b2ddc6c3296b82d601e966d5ac1959", "33c79b6dd6bc7f65079ab9ca5bebffb5f5d1141c689c9c6a7855776d1b09b7e8", "367385fc797b2c31564c427430c7a8630db1a00bd040555dfc1d5c52e39fcd72", "3c1884ff078fb8bf5f63d7d86921838b82ed4a7d0c027add773c2f38b3168754", "44e5240e8f4f8861d748f2a58b3f04daadab5e22bfec896bf5434745f788f33f", "46aa988e15f3ea72dddd81afe3839437b755fffddb5e173886f11460be909dce", "492e1e4df823b57f9334f591c78a1e0e65a361e92594534e0568eeeeea56bbba", "50fb9e25d25cfcb50b2e6842c4e104e4f0b424be4624e1724532bf005c67589a", "5ceadd60dbd1e56ab7faffbfee1df5ecb83c3f0420e47f652cd5306d70eb0296", "74d90d499c9c736d52dd6d9b7221af5665b9c04f1767e35f5dd8694324bd4601", "7eeac51fc37e6b19631a4b8e38b8261a074efcf7cc27fc16a6bee4697af7aaa5", "809c0a2ce9032cbcd7b5313f71af4bdc5c8c771cb86eb7559afd954cab82ebb5", "85d1ef2cdafd5507c4221d201aaf62fc9276f8b0f71bd3933363e62a33abc734", "8c3889c7681af77ecfa4431cd42a2885d093ecb811e81fbe5e203abc07e0995b", "9218d81b9fca98d2c47d35d688a0cea0c42fd473159dfd5612dcb0483c63e40b", "9319215530e236822169cbe92426cdc18d16b88c943fdf365a6309a89876e335", "96ec275c83bf839972d6a7dd7d685fdfb6a3233c3c382ecff839d04e7d53955d", "9aa4f3827992288edd37c9df345783a69ef58bd20cc02e64b36e44bcd157bbf1", "9d80f44137a70b6f84c750d11019a3419f409c944526a95219bea0ac31f4dd91", "b7ebd36128a2fe93991293f997e44be9286503c7530ace6a55b938b20be288d8", "c30857e1fbf7d4a4b79d7d376eefaf293ea4307b8293d00a62e6f517f51bfe9b", "c4c78e2c71c257c136cdd43869fd3d5e34fc2162dc22e4a5406b0ebe86958239", "c5472ea3945e8f9eb0659f37fc1f592fd06f4f725f0f03774a8999ad8c130334", "c6a842537f887be1fe115d8abb5daa9bc8cc124e455ff995830cc785624a97af", "cf0a2e040fdf5a6d95f4c286c6ef1df6b36c218b528c8a9158ec2452a804b9b8", "cfd28aad6fc61f7a5d4ee556a997dc6e5555d9381d1390c00ecaf984d57e4232", "d0fd1ec2e7c3e0aeaae999efe83f5d0f42c1160a1f8be5120d40857d20baa452", "dca5660e25932771460d4688ccbb515677caaf8595f3f3240ec16c117deff89a", "de7aedc85918c2f887886442e50f52c1b93545606317956d65f342bd81cb4fc3", "e6c0bbf8e277b74196e3140c35f9a1ae3eafd818f7f2d3a15819c49135d6c062"] pluggy = ["0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc", "b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c"] +prometheus-client = ["ee0c90350595e4a9f36591f291e6f9933246ea67d7cd7d1d6139a9781b14eaae"] psycopg2 = ["00cfecb3f3db6eb76dcc763e71777da56d12b6d61db6a2c6ccbbb0bff5421f8f", "076501fc24ae13b2609ba2303d88d4db79072562f0b8cc87ec1667dedff99dc1", "4e2b34e4c0ddfeddf770d7df93e269700b080a4d2ec514fec668d71895f56782", "5cacf21b6f813c239f100ef78a4132056f93a5940219ec25d2ef833cbeb05588", "61f58e9ecb9e4dc7e30be56b562f8fc10ae3addcfcef51b588eed10a5a66100d", "8954ff6e47247bdd134db602fcadfc21662835bd92ce0760f3842eacfeb6e0f3", "b6e8c854cdc623028e558a409b06ea2f16d13438335941c7765d0a42b5bedd33", "baca21c0f7344576346e260454d0007313ccca8c170684707a63946b27a56c8f", "bb1735378770fb95dbe392d29e71405d45c8bdcfa064f916504833a92ab03c55", "de3d3c46c1ee18f996db42d1eb44cf1565cc9e38fb1dbd9b773ff6b3fa8035d7", "dee885602bb200bdcb1d30f6da6c7bb207360bc786d0a364fe1540dd14af0bab"] py = ["64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa", "dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"] pycodestyle = ["95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", "e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"] @@ -1041,11 +1062,11 @@ python-decouple = ["1317df14b43efee4337a4aa02914bf004f010cd56d6c4bd894e6474ec8c4 python-dotenv = ["debd928b49dbc2bf68040566f55cdb3252458036464806f4094487244e2a4093", "f157d71d5fec9d4bd5f51c82746b6344dffa680ee85217c123f4a0c8117c4544"] python3-openid = ["0086da6b6ef3161cfe50fb1ee5cceaf2cda1700019fda03c2c5c440ca6abe4fa", "628d365d687e12da12d02c6691170f4451db28d6d68d050007e4a40065868502"] pytz = ["303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda", "d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141"] -pyyaml = ["1adecc22f88d38052fb787d959f003811ca858b799590a5eaa70e63dca50308c", "436bc774ecf7c103814098159fbb84c2715d25980175292c648f2da143909f95", "460a5a4248763f6f37ea225d19d5c205677d8d525f6a83357ca622ed541830c2", "5a22a9c84653debfbf198d02fe592c176ea548cccce47553f35f466e15cf2fd4", "7a5d3f26b89d688db27822343dfa25c599627bc92093e788956372285c6298ad", "9372b04a02080752d9e6f990179a4ab840227c6e2ce15b95e1278456664cf2ba", "a5dcbebee834eaddf3fa7366316b880ff4062e4bcc9787b78c7fbb4a26ff2dd1", "aee5bab92a176e7cd034e57f46e9df9a9862a71f8f37cad167c6fc74c65f5b4e", "c51f642898c0bacd335fc119da60baae0824f2cde95b0330b56c0553439f0673", "c68ea4d3ba1705da1e0d85da6684ac657912679a649e8868bd850d2c299cce13", "e23d0cc5299223dcc37885dae624f382297717e459ea24053709675a976a3e19"] +pyyaml = ["57acc1d8533cbe51f6662a55434f0dbecfa2b9eaf115bede8f6fd00115a0c0d3", "588c94b3d16b76cfed8e0be54932e5729cc185caffaa5a451e7ad2f7ed8b4043", "68c8dd247f29f9a0d09375c9c6b8fdc64b60810ebf07ba4cdd64ceee3a58c7b7", "70d9818f1c9cd5c48bb87804f2efc8692f1023dac7f1a1a5c61d454043c1d265", "86a93cccd50f8c125286e637328ff4eef108400dd7089b46a7be3445eecfa391", "a0f329125a926876f647c9fa0ef32801587a12328b4a3c741270464e3e4fa778", "a3c252ab0fa1bb0d5a3f6449a4826732f3eb6c0270925548cac342bc9b22c225", "b4bb4d3f5e232425e25dda21c070ce05168a786ac9eda43768ab7f3ac2770955", "cd0618c5ba5bda5f4039b9398bb7fb6a317bb8298218c3de25c47c4740e4b95e", "ceacb9e5f8474dcf45b940578591c7f3d960e82f926c707788a570b51ba59190", "fe6a88094b64132c4bb3b631412e90032e8cfe9745a58370462240b8cb7553cd"] requests = ["11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", "9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"] requests-oauthlib = ["bd6533330e8748e94bf0b214775fed487d309b8b8fe823dc45641ebcd9a32f57", "d3ed0c8f2e3bbc6b344fa63d6f933745ab394469da38db16bdddb461c7e25140", "dd5a0499abfefd087c6dd96693cbd5bfd28aa009719a7f85ab3fabe3956ef19a"] -"ruamel.yaml" = ["0372b039c2358db2e7a7d5c77f2a37c552bd45896936f430678b5f00ac9fef08", "12ebbba6447cfd61491ace18a6972d4edaab6615c933ae8a0016c27c27ee93b7", "1973a131a7b5a604653ffd904286e53334c08306b575bf964ae87f226d4956cd", "21c130c2741e8499ad92362d24f68aad70089e77b206e6e85d62e255e6017c92", "21c6287beabb96c9f51478506b00db269276d84aa41a61f29c88af397406b267", "2e07d11526c70bfb3a07c77d73c72b7fcfae21cc7f2252c7d6419e25b6349d23", "32755f26f46816e46bd74fd48c4db63ee500b68cbe2394b2943daab573d12a33", "343ace5ffbab036536a3da65e4cfd31b8292388a389f6305744984581a479b2a", "3de37e2de83fa4016886bf40069e9cc7816b51a1f57687e6872cb414f891a573", "809db4d5213bfb0205e2dcc6118fbfa348e3ae39f3d8f6810e3e6831f3070321", "8810319a64d4cf5701feb7c2833b03f8bd29e1d6753e4b97cf977f889eb6354d", "8b91affdcab942ab1319d8d24345a3de8e0e70fd2871d5bb61e19e32b5ae31d9", "8e3d51a2d20a982a6eb38b0b9bcecdd2acacdcf2137081b0a744ca98cdcca9af", "97cc58bff414bf40b8b909aeadaa669becad35e182a2b229d0c3d6c9a34e1f15", "a6f4f214d6dc6253846630af7a701ee00ebd2730eca03c7e8e031d748b0e22e4", "e1e34869d005e3e5292f44022e01cc4eb3f045291ba01166908f97f4716e7a69", "effc63d7f86841cec3a4a76ed6c73f441e68c5ea02c3dee3faaaeda9fabc8cd8", "fc4c69b505f9178aa55b780cd8cde7c17927501046c7f0ebcd66959cd0dba2db"] -s3transfer = ["7b9ad3213bff7d357f888e0fab5101b56fa1a0548ee77d121c3a3dbfbef4cb2e", "f23d5cb7d862b104401d9021fc82e5fa0e0cf57b7660a1331425aab0c691d021"] +"ruamel.yaml" = ["17dbf6b7362e7aee8494f7a0f5cffd44902a6331fe89ef0853b855a7930ab845", "23731c9efb79f3f5609dedffeb6c5c47a68125fd3d4b157d9fc71b1cd49076a9", "2bbdd598ae57bac20968cf9028cc67d37d83bdb7942a94b9478110bc72193148", "34586084cdd60845a3e1bece2b58f0a889be25450db8cc0ea143ddf0f40557a2", "35957fedbb287b01313bb5c556ffdc70c0277c3500213b5e73dfd8716f748d77", "414cb87a40974a575830b406ffab4ab8c6cbd82eeb73abd2a9d1397c1f0223e1", "428775be75db68d908b17e4e8dda424c410222f170dc173246aa63e972d094b3", "514f670f7d36519bda504d507edfe63e3c20489f86c86d42bc4d9a6dbdf82c7b", "5cb962c1ac6887c5da29138fbbe3b4b7705372eb54e599907fa63d4cd743246d", "5f6e30282cf70fb7754e1a5f101e27b5240009766376e131b31ab49f14fe81be", "86f8e010af6af0b4f42de2d0d9b19cb441e61d3416082186f9dd03c8552d13ad", "8d47ed1e557d546bd2dfe54f504d7274274602ff7a0652cde84c258ad6c2d96d", "98668876720bce1ac08562d8b93a564a80e3397e442c7ea19cebdcdf73da7f74", "9e1f0ddc18d8355dcf5586a5d90417df56074f237812b8682a93b62cca9d2043", "a7bc812a72a79d6b7dbb96fa5bee3950464b65ec055d3abc4db6572f2373a95c", "b72e13f9f206ee103247b07afd5a39c8b1aa98e8eba80ddba184d030337220ba", "bcff8ea9d916789e85e24beed8830c157fb8bc7c313e554733a8151540e66c01", "c76e78b3bab652069b8d6f7889b0e72f3455c2b854b2e0a8818393d149ad0a0d"] +s3transfer = ["6efc926738a3cd576c2a79725fed9afde92378aa5c6a957e3af010cb019fac9d", "b780f2411b824cb541dbcd2c713d0cb61c7d1bcadae204cdddda2b35cef493ba"] sentry-sdk = ["31adb5dc65ef35cabb48e3d7ad9804d885f22e74ef606a266beb31661d59a226", "52881e11649d7e5a0b04c026adcf473ab16d0ec4a1cf5e7259dd292c984264ef"] six = ["3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73"] smmap2 = ["0555a7bf4df71d1ef4218e4807bbf9b201f910174e6e08af2e138d4e517b4dde", "29a9ffa0497e7f2be94ca0ed1ca1aa3cd4cf25a1f6b4f5f87f74b46ed91d609a"] diff --git a/pyproject.toml b/pyproject.toml index 150e59c8..f90734a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,12 +17,15 @@ django-allauth = "^0.39.1" django-anymail = "^6.0" django-background-tasks = "^1.2" django-cors-headers = "^3.0" +django-prometheus = "^1.0" django-recaptcha2 = "^1.4" django-rest-auth = "^0.9.5" django-split-settings = "^0.3.0" django-storages = "^1.7" +django-suit = "^0.2.26" +django-suit-daterange-filter = "^0.0.6" django-widget-tweaks = "^1.4" -drf-yasg = "^1.15" +drf-yasg = "^1.16" gunicorn = "^19.9" pillow = "^6.0" psycopg2 = "^2.8" @@ -32,7 +35,6 @@ mandrill = "^1.0" python-dotenv = "^0.10.3" requests = "^2.21" sentry-sdk = "^0.9.0" -django-suit = "^0.2.26" [tool.poetry.dev-dependencies] bandit = "^1.6" diff --git a/src/api/admin.py b/src/api/admin.py index 12084f9f..1ca79071 100644 --- a/src/api/admin.py +++ b/src/api/admin.py @@ -53,6 +53,7 @@ class CodeSchoolAdmin(admin.ModelAdmin): "hardware_included", "has_online", "online_only", + "has_housing", "mooc", "is_partner", "rep_name", @@ -64,6 +65,7 @@ class CodeSchoolAdmin(admin.ModelAdmin): "hardware_included", "has_online", "online_only", + "has_housing", "mooc", "is_partner", ) diff --git a/src/api/migrations/0006_auto_20190610_1058.py b/src/api/migrations/0006_auto_20190610_1058.py new file mode 100644 index 00000000..fd48f4d8 --- /dev/null +++ b/src/api/migrations/0006_auto_20190610_1058.py @@ -0,0 +1,13 @@ +# Generated by Django 2.2.2 on 2019-06-10 15:58 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [("api", "0005_scholarship_scholarshipapplication")] + + operations = [ + migrations.DeleteModel(name="OldScholarship"), + migrations.DeleteModel(name="OldScholarshipApplication"), + ] diff --git a/src/api/migrations/0007_codeschool_has_housing.py b/src/api/migrations/0007_codeschool_has_housing.py new file mode 100644 index 00000000..904032ea --- /dev/null +++ b/src/api/migrations/0007_codeschool_has_housing.py @@ -0,0 +1,16 @@ +# Generated by Django 2.2.2 on 2019-06-11 00:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [("api", "0006_auto_20190610_1058")] + + operations = [ + migrations.AddField( + model_name="codeschool", + name="has_housing", + field=models.BooleanField(default=False, null=True), + ) + ] diff --git a/src/api/models.py b/src/api/models.py index 4aa30189..734e5692 100644 --- a/src/api/models.py +++ b/src/api/models.py @@ -1,14 +1,16 @@ from django.contrib.auth.models import User from django.db import models +from django_prometheus.models import ExportModelOperationsMixin -class CodeSchool(models.Model): +class CodeSchool(ExportModelOperationsMixin("code_school"), models.Model): name = models.CharField(max_length=256, blank=True, null=True) url = models.CharField(max_length=256, blank=True, null=True) logo = models.CharField(max_length=256, blank=True, null=True) full_time = models.BooleanField(blank=True, null=True) hardware_included = models.BooleanField(blank=True, null=True) has_online = models.BooleanField(blank=True, null=True) + has_housing = models.BooleanField(default=False, null=True) online_only = models.BooleanField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) @@ -25,7 +27,7 @@ class Meta: db_table = "api_code_schools" -class Location(models.Model): +class Location(ExportModelOperationsMixin("location"), models.Model): va_accepted = models.BooleanField(blank=True, null=True) address1 = models.CharField(max_length=256, blank=True, null=True) address2 = models.CharField(max_length=256, blank=True, null=True) @@ -47,7 +49,7 @@ class Meta: db_table = "api_locations" -class Scholarship(models.Model): +class Scholarship(ExportModelOperationsMixin("scholarship"), models.Model): name = models.CharField(max_length=256, blank=True, null=True) description = models.TextField(blank=True, null=True) location = models.CharField(max_length=256, blank=True, null=True) @@ -61,7 +63,9 @@ def __str__(self): return f"{self.name} - {self.location}" -class ScholarshipApplication(models.Model): +class ScholarshipApplication( + ExportModelOperationsMixin("scholarship_application"), models.Model +): reason = models.TextField(blank=True, null=True) terms_accepted = models.BooleanField(blank=True, null=True) user = models.ForeignKey(User, models.DO_NOTHING, blank=True, null=True) @@ -75,7 +79,7 @@ def __str__(self): return f"{self.user} - {self.scholarship}" -class TeamMember(models.Model): +class TeamMember(ExportModelOperationsMixin("team_member"), models.Model): name = models.CharField(max_length=256, blank=True, null=True) role = models.CharField(max_length=256, blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) diff --git a/src/core/admin.py b/src/core/admin.py index 07ce7e77..c51a1606 100644 --- a/src/core/admin.py +++ b/src/core/admin.py @@ -1,3 +1,4 @@ +from date_range_filter import DateRangeFilter from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User @@ -12,7 +13,7 @@ class ExtendedUserAdmin(UserAdmin): list_display = ("email", "first_name", "last_name", "is_staff", "is_superuser") @staticmethod - def suit_row_attributes(obj, request): + def suit_row_attributes(obj, request): # pragma: no cover """ Adds highlight to superusers in Users table """ @@ -21,7 +22,7 @@ def suit_row_attributes(obj, request): @admin.register(Profile) -class CodeSchoolAdmin(admin.ModelAdmin): +class ProfileAdmin(admin.ModelAdmin): list_display = ( "user", "zipcode", @@ -30,7 +31,16 @@ class CodeSchoolAdmin(admin.ModelAdmin): "state", "is_volunteer", "military_status", + "branch_of_service", + "created_at", ) - list_filter = ("is_mentor", "state", "is_volunteer", "military_status") + list_filter = ( + "is_mentor", + "state", + "is_volunteer", + "military_status", + "branch_of_service", + ("created_at", DateRangeFilter), + ) search_fields = ("user__email",) diff --git a/src/core/migrations/0008_profile_created_at.py b/src/core/migrations/0008_profile_created_at.py new file mode 100644 index 00000000..d00029f5 --- /dev/null +++ b/src/core/migrations/0008_profile_created_at.py @@ -0,0 +1,16 @@ +# Generated by Django 2.2.2 on 2019-06-10 15:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [("core", "0007_auto_20190603_1459")] + + operations = [ + migrations.AddField( + model_name="profile", + name="created_at", + field=models.DateTimeField(blank=True, null=True), + ) + ] diff --git a/src/core/migrations/0009_auto_20190610_1152.py b/src/core/migrations/0009_auto_20190610_1152.py new file mode 100644 index 00000000..49c9c15f --- /dev/null +++ b/src/core/migrations/0009_auto_20190610_1152.py @@ -0,0 +1,16 @@ +# Generated by Django 2.2.2 on 2019-06-10 16:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [("core", "0008_profile_created_at")] + + operations = [ + migrations.AlterField( + model_name="profile", + name="created_at", + field=models.DateTimeField(auto_now_add=True, null=True), + ) + ] diff --git a/src/core/models.py b/src/core/models.py index 5657d612..d212a9a0 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -2,14 +2,16 @@ from django.db import models from django.db.models.signals import post_save from django.dispatch import receiver +from django_prometheus.models import ExportModelOperationsMixin -class Profile(models.Model): +class Profile(ExportModelOperationsMixin("profile"), models.Model): """ Model used to extend Django's base User model """ user = models.OneToOneField(User, on_delete=models.CASCADE) + created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True) zipcode = models.CharField(max_length=512, blank=True, null=True) latitude = models.FloatField(blank=True, null=True) @@ -71,7 +73,7 @@ def create_profile(instance: User, created: bool, **kwargs: dict) -> None: Profile.objects.create(user=instance) -class OldUserObj(models.Model): +class OldUserObj(ExportModelOperationsMixin("old_user"), models.Model): email = models.CharField(unique=True, max_length=256, blank=True, null=True) zip = models.CharField(max_length=256, blank=True, null=True) latitude = models.FloatField(blank=True, null=True) diff --git a/src/core/urls.py b/src/core/urls.py index 761be730..9f7f9810 100644 --- a/src/core/urls.py +++ b/src/core/urls.py @@ -46,4 +46,5 @@ name="account_email_verification_sent", ), path("auth/", include("rest_auth.urls")), + path("", include("django_prometheus.urls")), ] diff --git a/src/frontend/templates/frontend/base.html b/src/frontend/templates/frontend/base.html index 48a2930e..e4645dbf 100644 --- a/src/frontend/templates/frontend/base.html +++ b/src/frontend/templates/frontend/base.html @@ -31,6 +31,9 @@ +