This commit is contained in:
Sonny Bakker 2023-07-02 10:23:16 +02:00
parent 6b2c4996d5
commit 8e7b059ad3
97 changed files with 15077 additions and 6892 deletions

2
.gitignore vendored
View file

@ -115,7 +115,7 @@ celerybeat-schedule
*.sage.py
# Environments
.env
*.env
.venv
env/
venv/

View file

@ -3,7 +3,6 @@ stages:
- test
- lint
- release
- deploy
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
@ -17,9 +16,8 @@ variables:
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- .venv/
- env/
- .cache/pip
- .cache/poetry
- node_modules/
include:
@ -27,4 +25,3 @@ include:
- local: '/gitlab-ci/test.yml'
- local: '/gitlab-ci/lint.yml'
- local: '/gitlab-ci/release.yml'
- local: '/gitlab-ci/deploy.yml'

View file

@ -1,5 +1,14 @@
# Changelog
## 0.4.0
- Add Makefile & use `pip-tools` to generate dependencies
- Add `pyproject.toml`
- Update dependencies
- Update docker-compose setup
- Default to `newsreader.conf.docker` settings module
- Add scroll to top/bottom buttons
## 0.3.13.8
- Update dependencies

45
Makefile Normal file
View file

@ -0,0 +1,45 @@
# Note: run this file from within your virtualenv!
#
#
# Build dependencies
build:
pip-compile \
--resolver=backtracking \
--output-file=requirements/base.txt \
pyproject.toml
# testing
pip-compile \
--resolver=backtracking \
--extra=testing \
--output-file=requirements/testing.txt \
requirements/base.txt \
pyproject.toml
# development
pip-compile \
--resolver=backtracking \
--extra=testing \
--extra=development \
--output-file=requirements/development.txt \
requirements/base.txt \
requirements/testing.txt \
pyproject.toml
# ci
pip-compile \
--resolver=backtracking \
--extra=testing \
--extra=ci \
--output-file=requirements/ci.txt \
requirements/base.txt \
requirements/testing.txt \
pyproject.toml
# production
pip-compile \
--resolver=backtracking \
--extra=production \
--output-file=requirements/production.txt \
requirements/base.txt \
pyproject.toml

5
bin/docker-entrypoint.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
python /app/src/manage.py migrate
exec "$@"

View file

@ -0,0 +1,22 @@
upstream gunicorn {
server django:8000;
}
server {
listen 80;
server_name localhost;
access_log /var/log/nginx/access_log;
error_log /var/log/nginx/error_log;
location /static/ {
root /app;
}
location / {
proxy_pass http://gunicorn;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}
}

View file

@ -0,0 +1,34 @@
version: "3.6"
volumes:
static-files:
node-modules:
services:
celery:
build:
target: development
volumes:
- ./src/:/app/src
django:
build:
target: development
command: python /app/src/manage.py runserver 0.0.0.0:8000
ports:
- "${DJANGO_PORT:-8000}:8000"
volumes:
- ./src:/app/src
- static-files:/app/src/newsreader/static
stdin_open: true
tty: true
webpack:
build:
context: .
dockerfile: ./docker/webpack
command: npm run build:watch
volumes:
- ./src/:/app/src
- static-files:/app/src/newsreader/static
- node-modules:/app/node_modules

View file

@ -0,0 +1,19 @@
version: "3.6"
volumes:
logs:
static-files:
services:
nginx:
image: nginx:1.23
depends_on:
django:
condition: service_healthy
ports:
# Note that --env-file should be used to set these correctly
- "${NGINX_HTTP_PORT:-80}:80"
volumes:
- ./config/nginx/conf.d:/etc/nginx/conf.d
- logs:/var/log/nginx
- static-files:/app/static

View file

@ -1,62 +1,102 @@
version: "3"
version: "3.6"
volumes:
logs:
media:
postgres-data:
static-files:
node-modules:
x-db-env: &db-env
POSTGRES_HOST:
POSTGRES_PORT:
POSTGRES_DB:
POSTGRES_USER:
POSTGRES_PASSWORD:
x-django-env: &django-env
<<: *db-env
DJANGO_SECRET_KEY:
DJANGO_SETTINGS_MODULE:
services:
db:
image: postgres
environment:
POSTGRES_DB: "newsreader"
POSTGRES_USER: "newsreader"
POSTGRES_PASSWORD: "newsreader"
<<: *db-env
image: postgres:15
healthcheck:
# Note that --env-file should be used to set these correctly
test: /usr/bin/pg_isready --username="${POSTGRES_USER}" --dbname="${POSTGRES_DB}"
interval: 5s
timeout: 10s
retries: 10
volumes:
- postgres-data:/var/lib/postgresql/data
rabbitmq:
image: rabbitmq:3.7
image: rabbitmq:3.12
memcached:
image: memcached:1.6
ports:
- "11211:11211"
entrypoint:
- memcached
- -m 64
celery:
build:
context: .
dockerfile: ./docker/django
command: celery worker -n worker1@%h -n worker2@%h --app newsreader --loglevel INFO --concurrency 2 --workdir /app/src/ --beat --scheduler django
target: production
args:
<<: *django-env
environment:
- DJANGO_SETTINGS_MODULE=newsreader.conf.docker
<<: *django-env
command: |
celery --app newsreader
--workdir /app/src/
worker --loglevel INFO
--concurrency 2
--beat
--scheduler django
-n worker1@%h
-n worker2@%h
depends_on:
- rabbitmq
- memcached
rabbitmq:
condition: service_started
memcached:
condition: service_started
db:
condition: service_healthy
django:
condition: service_healthy
volumes:
- .:/app
- logs:/app/logs
django:
build:
context: .
dockerfile: ./docker/django
command: python /app/src/manage.py runserver 0.0.0.0:8000
target: production
args:
<<: *django-env
environment:
- DJANGO_SETTINGS_MODULE=newsreader.conf.docker
ports:
- "8000:8000"
<<: *django-env
entrypoint: /app/bin/docker-entrypoint.sh
command: |
gunicorn --bind 0.0.0.0:8000
--workers 3
--chdir /app/src/
newsreader.wsgi:application
healthcheck:
test: /usr/bin/curl --fail http://django:8000 || exit 1
interval: 30s
timeout: 10s
retries: 10
depends_on:
- db
- memcached
memcached:
condition: service_started
db:
condition: service_healthy
volumes:
- .:/app
- static-files:/app/src/newsreader/static
stdin_open: true
tty: true
webpack:
build:
context: .
dockerfile: ./docker/webpack
command: npm run build:watch
volumes:
- .:/app
- static-files:/app/src/newsreader/static
- node-modules:/app/node_modules
- logs:/app/logs
- media:/app/media
- static-files:/app/static

View file

@ -1,10 +1,108 @@
FROM python:3.7-buster
# stage 1
FROM python:3.9-bullseye as backend
RUN pip install poetry
RUN apt-get update && apt-get install -y --no-install-recommends \
vim \
curl \
gettext \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY poetry.lock pyproject.toml /app/
RUN mkdir /app/src
RUN mkdir /app/logs
RUN mkdir /app/media
RUN poetry config virtualenvs.create false && poetry install --no-interaction --extras sentry
COPY ./requirements /app/requirements
COPY . /app/
RUN pip install -r requirements/base.txt
# stage 2
FROM node:16-bullseye AS frontend-build
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY ./*.json ./*.js ./.babelrc /app/
RUN npm ci
COPY ./src /app/src
RUN npm run build
# stage 3
FROM python:3.9-bullseye as production
RUN apt-get update && apt-get install -y --no-install-recommends \
postgresql-client \
vim \
curl \
gettext \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN mkdir /app/logs
RUN mkdir /app/media
RUN mkdir /app/bin
COPY --from=backend /usr/local/lib/python3.9 /usr/local/lib/python3.9
COPY --from=backend /usr/local/bin/celery /usr/local/bin/celery
COPY ./bin/docker-entrypoint.sh /app/bin/docker-entrypoint.sh
COPY --from=frontend-build /app/src/newsreader/static /app/src/newsreader/static
COPY ./src /app/src
COPY ./requirements /app/requirements
RUN pip install -r requirements/production.txt
RUN useradd -M -u 1000 newsreader
RUN chown -R newsreader:newsreader /app
USER newsreader
ARG POSTGRES_HOST
ARG POSTGRES_PORT
ARG POSTGRES_DB
ARG POSTGRES_USER
ARG POSTGRES_PASSWORD
ARG DJANGO_SECRET_KEY
ARG DJANGO_SETTINGS_MODULE
RUN python src/manage.py collectstatic --noinput
# (optional) stage 4
FROM python:3.9-bullseye as development
RUN apt-get update && apt-get install -y --no-install-recommends \
vim \
curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN mkdir /app/logs
RUN mkdir /app/media
RUN mkdir /app/bin
COPY ./requirements /app/requirements
COPY ./bin/docker-entrypoint.sh /app/bin/docker-entrypoint.sh
COPY --from=backend /usr/local/lib/python3.9 /usr/local/lib/python3.9
COPY --from=backend /usr/local/bin/celery /usr/local/bin/celery
COPY --from=backend /app/src/ /app/src/
RUN pip install -r requirements/development.txt
RUN useradd -M -u 1000 newsreader
RUN chown -R newsreader:newsreader /app
USER newsreader

View file

@ -1,9 +1,10 @@
FROM node:12
FROM node:16-bullseye
WORKDIR /app
RUN mkdir /app/src
COPY package.json package-lock.json /app/
COPY package*.json webpack.*.js .babelrc /app/
RUN npm install
COPY . /app/
COPY ./src /app/src

View file

@ -1,6 +1,6 @@
static:
stage: build
image: node:12
image: node:16-bullseye
before_script:
- npm install
script:

View file

@ -1,22 +0,0 @@
deploy:
stage: deploy
image: python:3.7
environment:
name: production
url: rss.fudiggity.nl
rules:
- if: $CI_COMMIT_TAG
before_script:
- pip install ansible --quiet
- git clone https://git.fudiggity.nl/ansible/newsreader.git deployment --branch master
- cd deployment
- ansible-galaxy install -r requirements.yml
- mkdir /root/.ssh && echo "$DEPLOY_HOST_KEY" > /root/.ssh/known_hosts
- echo "$DEPLOY_KEY" > deploy_key && chmod 0600 deploy_key
- echo "$VAULT_PASSWORD" > vault
script:
- >
ansible-playbook playbook.yml
--private-key deploy_key
--vault-password-file vault
--extra-vars "app_branch=$CI_COMMIT_TAG"

View file

@ -1,15 +1,12 @@
python-linting:
stage: lint
image: python:3.7
image: python:3.9-bullseye
before_script:
- pip install poetry --quiet
- poetry config cache-dir ~/.cache/poetry
- poetry config virtualenvs.in-project true
- poetry install --no-interaction --quiet
- pip install -r requirements/ci.txt
script:
- poetry run isort src/ --check-only --recursive
- poetry run black src/ --line-length 88 --check
- poetry run autoflake src/ --check --recursive --remove-all-unused-imports --ignore-init-module-imports
- isort src/ --check-only
- black src/ --line-length 88 --check
- autoflake src/ --check --recursive --remove-all-unused-imports --ignore-init-module-imports
only:
refs:
- development
@ -17,7 +14,7 @@ python-linting:
javascript-linting:
stage: lint
image: node:12
image: node:16-bullseye
before_script:
- npm install
script:

View file

@ -2,21 +2,17 @@ python-tests:
stage: test
coverage: '/TOTAL\s+\d+\s+\d+\s+(\d+%)/'
services:
- postgres:11
- postgres:15
- memcached:1.5.22
image: python:3.7
image: python:3.9-bullseye
before_script:
- pip install poetry --quiet
- poetry config cache-dir .cache/poetry
- poetry config virtualenvs.in-project true
- poetry install --no-interaction --quiet --extras sentry
- pip install -r requirements/ci.txt
script:
- poetry run coverage run src/manage.py test newsreader
- poetry run coverage report
- coverage run ./src/manage.py test newsreader
javascript-tests:
stage: test
image: node:12
image: node:16-bullseye
before_script:
- npm install
script:

13348
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -48,11 +48,11 @@
"jest": "^24.9.0",
"mini-css-extract-plugin": "^0.9.0",
"node-fetch": "^2.6.1",
"node-sass": "^4.14.1",
"prettier": "^1.19.1",
"react": "^16.14.0",
"react-dom": "^16.14.0",
"redux-mock-store": "^1.5.4",
"sass": "^1.52.1",
"sass-loader": "^8.0.2",
"style-loader": "^1.3.0",
"url-loader": "^4.1.1",

1298
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,46 +1,50 @@
[tool.poetry]
name = "newsreader"
version = "0.3.13.8"
description = "Webapplication for reading RSS feeds"
authors = ["Sonny <sonnyba871@gmail.com>"]
license = "GPL-3.0"
[project]
name = 'newsreader'
version = '0.4.0.0'
authors = [{name = 'Sonny', email= 'sonnyba871@gmail.com'}]
license = {text = 'GPL-3.0'}
requires-python = '>=3.11'
dependencies = [
'django~=3.2',
'celery~=5.0',
'psycopg2',
[tool.poetry.dependencies]
python = "^3.7"
bleach = "^3.1.4"
Django = "^3.2"
celery = "^4.4.2"
beautifulsoup4 = "^4.9.0"
django-axes = "^5.3.1"
django-celery-beat = "^2.0.0"
djangorestframework = "^3.11.0"
drf-yasg = "^1.17.1"
django-registration-redux = "^2.7"
lxml = "^4.5.0"
feedparser = "^5.2.1"
python-memcached = "^1.59"
requests = "^2.23.0"
psycopg2-binary = "^2.8.5"
gunicorn = "^20.0.4"
python-dotenv = "^0.12.0"
sentry-sdk = {version = "^1.0.0", optional = true}
ftfy = "^5.8"
requests_oauthlib = "^1.3.0"
'django-axes',
'django-celery-beat~=2.5.0',
'django-registration-redux~=2.7',
'django-rest-framework',
'drf-yasg',
[tool.poetry.extras]
sentry = ["sentry_sdk"]
'python-memcached',
'python-dotenv~=0.12',
[tool.poetry.dev-dependencies]
factory-boy = "^2.12.0"
freezegun = "^0.3.15"
django-debug-toolbar = "^2.2"
django-extensions = "^2.2.9"
black = "19.3b0"
isort = "4.3.21"
autoflake = "1.3.1"
tblib = "1.6.0"
coverage = "^5.1"
'ftfy~=5.8',
[build-system]
requires = ["poetry>=1.0.10"]
build-backend = "poetry.masonry.api"
'requests',
'requests_oauthlib',
'feedparser',
'bleach',
'beautifulsoup4',
'lxml'
]
[project.optional-dependencies]
testing = [
'factory-boy',
'freezegun',
'black',
'isort',
'autoflake',
'tblib',
]
development = [
'pip-tools>=6.13.0',
'django-debug-toolbar',
'django-extensions',
]
ci = ['coverage>=5.3.1']
production = ['gunicorn~=20.0', 'sentry-sdk~=1.0']

143
requirements/base.txt Normal file
View file

@ -0,0 +1,143 @@
#
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# pip-compile --output-file=requirements/base.txt --resolver=backtracking pyproject.toml
#
amqp==5.1.1
# via kombu
asgiref==3.7.2
# via django
beautifulsoup4==4.12.2
# via newsreader (pyproject.toml)
billiard==4.1.0
# via celery
bleach==6.0.0
# via newsreader (pyproject.toml)
celery==5.3.1
# via
# django-celery-beat
# newsreader (pyproject.toml)
certifi==2023.5.7
# via requests
charset-normalizer==3.1.0
# via requests
click==8.1.3
# via
# celery
# click-didyoumean
# click-plugins
# click-repl
click-didyoumean==0.3.0
# via celery
click-plugins==1.1.1
# via celery
click-repl==0.3.0
# via celery
cron-descriptor==1.4.0
# via django-celery-beat
django==3.2.19
# via
# django-axes
# django-celery-beat
# django-timezone-field
# djangorestframework
# drf-yasg
# newsreader (pyproject.toml)
django-axes==6.0.4
# via newsreader (pyproject.toml)
django-celery-beat==2.5.0
# via newsreader (pyproject.toml)
django-registration-redux==2.12
# via newsreader (pyproject.toml)
django-rest-framework==0.1.0
# via newsreader (pyproject.toml)
django-timezone-field==5.1
# via django-celery-beat
djangorestframework==3.14.0
# via
# django-rest-framework
# drf-yasg
drf-yasg==1.21.6
# via newsreader (pyproject.toml)
feedparser==6.0.10
# via newsreader (pyproject.toml)
ftfy==5.9
# via newsreader (pyproject.toml)
idna==3.4
# via requests
inflection==0.5.1
# via drf-yasg
kombu==5.3.1
# via celery
lxml==4.9.2
# via newsreader (pyproject.toml)
oauthlib==3.2.2
# via requests-oauthlib
packaging==23.1
# via drf-yasg
prompt-toolkit==3.0.38
# via click-repl
psycopg2==2.9.6
# via newsreader (pyproject.toml)
python-crontab==2.7.1
# via django-celery-beat
python-dateutil==2.8.2
# via
# celery
# python-crontab
python-dotenv==0.21.1
# via newsreader (pyproject.toml)
python-memcached==1.59
# via newsreader (pyproject.toml)
pytz==2023.3
# via
# django
# django-timezone-field
# djangorestframework
# drf-yasg
pyyaml==6.0
# via drf-yasg
requests==2.31.0
# via
# newsreader (pyproject.toml)
# requests-oauthlib
requests-oauthlib==1.3.1
# via newsreader (pyproject.toml)
sgmllib3k==1.0.0
# via feedparser
six==1.16.0
# via
# bleach
# python-dateutil
# python-memcached
soupsieve==2.4.1
# via beautifulsoup4
sqlparse==0.4.4
# via django
typing-extensions==4.6.3
# via
# asgiref
# kombu
tzdata==2023.3
# via
# celery
# django-celery-beat
uritemplate==4.1.1
# via drf-yasg
urllib3==2.0.3
# via requests
vine==5.0.0
# via
# amqp
# celery
# kombu
wcwidth==0.2.6
# via
# ftfy
# prompt-toolkit
webencodings==0.5.1
# via bleach
# The following packages are considered to be unsafe in a requirements file:
# setuptools

320
requirements/ci.txt Normal file
View file

@ -0,0 +1,320 @@
#
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# pip-compile --extra=ci --extra=testing --output-file=requirements/ci.txt --resolver=backtracking pyproject.toml requirements/base.txt requirements/testing.txt
#
amqp==5.1.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# kombu
asgiref==3.7.2
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django
autoflake==2.2.0
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
beautifulsoup4==4.12.2
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
billiard==4.1.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
black==23.3.0
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
bleach==6.0.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
celery==5.3.1
# via
# -r requirements/base.txt
# django-celery-beat
# newsreader (pyproject.toml)
certifi==2023.5.7
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# requests
charset-normalizer==3.1.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# requests
click==8.1.3
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# black
# celery
# click-didyoumean
# click-plugins
# click-repl
click-didyoumean==0.3.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
click-plugins==1.1.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
click-repl==0.3.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
coverage==7.2.7
# via newsreader (pyproject.toml)
cron-descriptor==1.4.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django-celery-beat
django==3.2.19
# via
# -r requirements/base.txt
# django-axes
# django-celery-beat
# django-timezone-field
# djangorestframework
# drf-yasg
# newsreader (pyproject.toml)
django-axes==6.0.4
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-celery-beat==2.5.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-registration-redux==2.12
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-rest-framework==0.1.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-timezone-field==5.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django-celery-beat
djangorestframework==3.14.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django-rest-framework
# drf-yasg
drf-yasg==1.21.6
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
factory-boy==3.2.1
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
faker==18.11.2
# via
# -r requirements/testing.txt
# factory-boy
feedparser==6.0.10
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
freezegun==1.2.2
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
ftfy==5.9
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
idna==3.4
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# requests
inflection==0.5.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# drf-yasg
isort==5.12.0
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
kombu==5.3.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
lxml==4.9.2
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
mypy-extensions==1.0.0
# via
# -r requirements/testing.txt
# black
oauthlib==3.2.2
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# requests-oauthlib
packaging==23.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# black
# drf-yasg
pathspec==0.11.1
# via
# -r requirements/testing.txt
# black
platformdirs==3.8.0
# via
# -r requirements/testing.txt
# black
prompt-toolkit==3.0.38
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# click-repl
psycopg2==2.9.6
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
pyflakes==3.0.1
# via
# -r requirements/testing.txt
# autoflake
python-crontab==2.7.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django-celery-beat
python-dateutil==2.8.2
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
# faker
# freezegun
# python-crontab
python-dotenv==0.21.1
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
python-memcached==1.59
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
pytz==2023.3
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django
# django-timezone-field
# djangorestframework
# drf-yasg
pyyaml==6.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# drf-yasg
requests==2.31.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
# requests-oauthlib
requests-oauthlib==1.3.1
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
sgmllib3k==1.0.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# feedparser
six==1.16.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# bleach
# python-dateutil
# python-memcached
soupsieve==2.4.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# beautifulsoup4
sqlparse==0.4.4
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django
tblib==2.0.0
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
tomli==2.0.1
# via
# -r requirements/testing.txt
# autoflake
# black
typing-extensions==4.6.3
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# asgiref
# black
# kombu
tzdata==2023.3
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
# django-celery-beat
uritemplate==4.1.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# drf-yasg
urllib3==2.0.3
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# requests
vine==5.0.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# amqp
# celery
# kombu
wcwidth==0.2.6
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# ftfy
# prompt-toolkit
webencodings==0.5.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# bleach
# The following packages are considered to be unsafe in a requirements file:
# setuptools

View file

@ -0,0 +1,338 @@
#
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# pip-compile --extra=development --extra=testing --output-file=requirements/development.txt --resolver=backtracking pyproject.toml requirements/base.txt requirements/testing.txt
#
amqp==5.1.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# kombu
asgiref==3.7.2
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django
autoflake==2.2.0
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
beautifulsoup4==4.12.2
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
billiard==4.1.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
black==23.3.0
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
bleach==6.0.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
build==0.10.0
# via pip-tools
celery==5.3.1
# via
# -r requirements/base.txt
# django-celery-beat
# newsreader (pyproject.toml)
certifi==2023.5.7
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# requests
charset-normalizer==3.1.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# requests
click==8.1.3
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# black
# celery
# click-didyoumean
# click-plugins
# click-repl
# pip-tools
click-didyoumean==0.3.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
click-plugins==1.1.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
click-repl==0.3.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
cron-descriptor==1.4.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django-celery-beat
django==3.2.19
# via
# -r requirements/base.txt
# django-axes
# django-celery-beat
# django-debug-toolbar
# django-extensions
# django-timezone-field
# djangorestframework
# drf-yasg
# newsreader (pyproject.toml)
django-axes==6.0.4
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-celery-beat==2.5.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-debug-toolbar==4.1.0
# via newsreader (pyproject.toml)
django-extensions==3.2.3
# via newsreader (pyproject.toml)
django-registration-redux==2.12
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-rest-framework==0.1.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-timezone-field==5.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django-celery-beat
djangorestframework==3.14.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django-rest-framework
# drf-yasg
drf-yasg==1.21.6
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
factory-boy==3.2.1
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
faker==18.11.2
# via
# -r requirements/testing.txt
# factory-boy
feedparser==6.0.10
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
freezegun==1.2.2
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
ftfy==5.9
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
idna==3.4
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# requests
inflection==0.5.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# drf-yasg
isort==5.12.0
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
kombu==5.3.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
lxml==4.9.2
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
mypy-extensions==1.0.0
# via
# -r requirements/testing.txt
# black
oauthlib==3.2.2
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# requests-oauthlib
packaging==23.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# black
# build
# drf-yasg
pathspec==0.11.1
# via
# -r requirements/testing.txt
# black
pip-tools==6.13.0
# via newsreader (pyproject.toml)
platformdirs==3.8.0
# via
# -r requirements/testing.txt
# black
prompt-toolkit==3.0.38
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# click-repl
psycopg2==2.9.6
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
pyflakes==3.0.1
# via
# -r requirements/testing.txt
# autoflake
pyproject-hooks==1.0.0
# via build
python-crontab==2.7.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django-celery-beat
python-dateutil==2.8.2
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
# faker
# freezegun
# python-crontab
python-dotenv==0.21.1
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
python-memcached==1.59
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
pytz==2023.3
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django
# django-timezone-field
# djangorestframework
# drf-yasg
pyyaml==6.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# drf-yasg
requests==2.31.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
# requests-oauthlib
requests-oauthlib==1.3.1
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
sgmllib3k==1.0.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# feedparser
six==1.16.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# bleach
# python-dateutil
# python-memcached
soupsieve==2.4.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# beautifulsoup4
sqlparse==0.4.4
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# django
# django-debug-toolbar
tblib==2.0.0
# via
# -r requirements/testing.txt
# newsreader (pyproject.toml)
tomli==2.0.1
# via
# -r requirements/testing.txt
# autoflake
# black
# build
# pyproject-hooks
typing-extensions==4.6.3
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# asgiref
# black
# kombu
tzdata==2023.3
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# celery
# django-celery-beat
uritemplate==4.1.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# drf-yasg
urllib3==2.0.3
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# requests
vine==5.0.0
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# amqp
# celery
# kombu
wcwidth==0.2.6
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# ftfy
# prompt-toolkit
webencodings==0.5.1
# via
# -r requirements/base.txt
# -r requirements/testing.txt
# bleach
wheel==0.40.0
# via pip-tools
# The following packages are considered to be unsafe in a requirements file:
# pip
# setuptools

237
requirements/production.txt Normal file
View file

@ -0,0 +1,237 @@
#
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# pip-compile --extra=production --output-file=requirements/production.txt --resolver=backtracking pyproject.toml requirements/base.txt
#
amqp==5.1.1
# via
# -r requirements/base.txt
# kombu
asgiref==3.7.2
# via
# -r requirements/base.txt
# django
beautifulsoup4==4.12.2
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
billiard==4.1.0
# via
# -r requirements/base.txt
# celery
bleach==6.0.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
celery==5.3.1
# via
# -r requirements/base.txt
# django-celery-beat
# newsreader (pyproject.toml)
certifi==2023.5.7
# via
# -r requirements/base.txt
# requests
# sentry-sdk
charset-normalizer==3.1.0
# via
# -r requirements/base.txt
# requests
click==8.1.3
# via
# -r requirements/base.txt
# celery
# click-didyoumean
# click-plugins
# click-repl
click-didyoumean==0.3.0
# via
# -r requirements/base.txt
# celery
click-plugins==1.1.1
# via
# -r requirements/base.txt
# celery
click-repl==0.3.0
# via
# -r requirements/base.txt
# celery
cron-descriptor==1.4.0
# via
# -r requirements/base.txt
# django-celery-beat
django==3.2.19
# via
# -r requirements/base.txt
# django-axes
# django-celery-beat
# django-timezone-field
# djangorestframework
# drf-yasg
# newsreader (pyproject.toml)
django-axes==6.0.4
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-celery-beat==2.5.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-registration-redux==2.12
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-rest-framework==0.1.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-timezone-field==5.1
# via
# -r requirements/base.txt
# django-celery-beat
djangorestframework==3.14.0
# via
# -r requirements/base.txt
# django-rest-framework
# drf-yasg
drf-yasg==1.21.6
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
feedparser==6.0.10
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
ftfy==5.9
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
gunicorn==20.1.0
# via newsreader (pyproject.toml)
idna==3.4
# via
# -r requirements/base.txt
# requests
inflection==0.5.1
# via
# -r requirements/base.txt
# drf-yasg
kombu==5.3.1
# via
# -r requirements/base.txt
# celery
lxml==4.9.2
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
oauthlib==3.2.2
# via
# -r requirements/base.txt
# requests-oauthlib
packaging==23.1
# via
# -r requirements/base.txt
# drf-yasg
prompt-toolkit==3.0.38
# via
# -r requirements/base.txt
# click-repl
psycopg2==2.9.6
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
python-crontab==2.7.1
# via
# -r requirements/base.txt
# django-celery-beat
python-dateutil==2.8.2
# via
# -r requirements/base.txt
# celery
# python-crontab
python-dotenv==0.21.1
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
python-memcached==1.59
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
pytz==2023.3
# via
# -r requirements/base.txt
# django
# django-timezone-field
# djangorestframework
# drf-yasg
pyyaml==6.0
# via
# -r requirements/base.txt
# drf-yasg
requests==2.31.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
# requests-oauthlib
requests-oauthlib==1.3.1
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
sentry-sdk==1.26.0
# via newsreader (pyproject.toml)
sgmllib3k==1.0.0
# via
# -r requirements/base.txt
# feedparser
six==1.16.0
# via
# -r requirements/base.txt
# bleach
# python-dateutil
# python-memcached
soupsieve==2.4.1
# via
# -r requirements/base.txt
# beautifulsoup4
sqlparse==0.4.4
# via
# -r requirements/base.txt
# django
typing-extensions==4.6.3
# via
# -r requirements/base.txt
# asgiref
# kombu
tzdata==2023.3
# via
# -r requirements/base.txt
# celery
# django-celery-beat
uritemplate==4.1.1
# via
# -r requirements/base.txt
# drf-yasg
urllib3==2.0.3
# via
# -r requirements/base.txt
# requests
# sentry-sdk
vine==5.0.0
# via
# -r requirements/base.txt
# amqp
# celery
# kombu
wcwidth==0.2.6
# via
# -r requirements/base.txt
# ftfy
# prompt-toolkit
webencodings==0.5.1
# via
# -r requirements/base.txt
# bleach
# The following packages are considered to be unsafe in a requirements file:
# setuptools

262
requirements/testing.txt Normal file
View file

@ -0,0 +1,262 @@
#
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# pip-compile --extra=testing --output-file=requirements/testing.txt --resolver=backtracking pyproject.toml requirements/base.txt
#
amqp==5.1.1
# via
# -r requirements/base.txt
# kombu
asgiref==3.7.2
# via
# -r requirements/base.txt
# django
autoflake==2.2.0
# via newsreader (pyproject.toml)
beautifulsoup4==4.12.2
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
billiard==4.1.0
# via
# -r requirements/base.txt
# celery
black==23.3.0
# via newsreader (pyproject.toml)
bleach==6.0.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
celery==5.3.1
# via
# -r requirements/base.txt
# django-celery-beat
# newsreader (pyproject.toml)
certifi==2023.5.7
# via
# -r requirements/base.txt
# requests
charset-normalizer==3.1.0
# via
# -r requirements/base.txt
# requests
click==8.1.3
# via
# -r requirements/base.txt
# black
# celery
# click-didyoumean
# click-plugins
# click-repl
click-didyoumean==0.3.0
# via
# -r requirements/base.txt
# celery
click-plugins==1.1.1
# via
# -r requirements/base.txt
# celery
click-repl==0.3.0
# via
# -r requirements/base.txt
# celery
cron-descriptor==1.4.0
# via
# -r requirements/base.txt
# django-celery-beat
django==3.2.19
# via
# -r requirements/base.txt
# django-axes
# django-celery-beat
# django-timezone-field
# djangorestframework
# drf-yasg
# newsreader (pyproject.toml)
django-axes==6.0.4
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-celery-beat==2.5.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-registration-redux==2.12
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-rest-framework==0.1.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
django-timezone-field==5.1
# via
# -r requirements/base.txt
# django-celery-beat
djangorestframework==3.14.0
# via
# -r requirements/base.txt
# django-rest-framework
# drf-yasg
drf-yasg==1.21.6
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
factory-boy==3.2.1
# via newsreader (pyproject.toml)
faker==18.11.2
# via factory-boy
feedparser==6.0.10
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
freezegun==1.2.2
# via newsreader (pyproject.toml)
ftfy==5.9
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
idna==3.4
# via
# -r requirements/base.txt
# requests
inflection==0.5.1
# via
# -r requirements/base.txt
# drf-yasg
isort==5.12.0
# via newsreader (pyproject.toml)
kombu==5.3.1
# via
# -r requirements/base.txt
# celery
lxml==4.9.2
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
mypy-extensions==1.0.0
# via black
oauthlib==3.2.2
# via
# -r requirements/base.txt
# requests-oauthlib
packaging==23.1
# via
# -r requirements/base.txt
# black
# drf-yasg
pathspec==0.11.1
# via black
platformdirs==3.8.0
# via black
prompt-toolkit==3.0.38
# via
# -r requirements/base.txt
# click-repl
psycopg2==2.9.6
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
pyflakes==3.0.1
# via autoflake
python-crontab==2.7.1
# via
# -r requirements/base.txt
# django-celery-beat
python-dateutil==2.8.2
# via
# -r requirements/base.txt
# celery
# faker
# freezegun
# python-crontab
python-dotenv==0.21.1
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
python-memcached==1.59
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
pytz==2023.3
# via
# -r requirements/base.txt
# django
# django-timezone-field
# djangorestframework
# drf-yasg
pyyaml==6.0
# via
# -r requirements/base.txt
# drf-yasg
requests==2.31.0
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
# requests-oauthlib
requests-oauthlib==1.3.1
# via
# -r requirements/base.txt
# newsreader (pyproject.toml)
sgmllib3k==1.0.0
# via
# -r requirements/base.txt
# feedparser
six==1.16.0
# via
# -r requirements/base.txt
# bleach
# python-dateutil
# python-memcached
soupsieve==2.4.1
# via
# -r requirements/base.txt
# beautifulsoup4
sqlparse==0.4.4
# via
# -r requirements/base.txt
# django
tblib==2.0.0
# via newsreader (pyproject.toml)
tomli==2.0.1
# via
# autoflake
# black
typing-extensions==4.6.3
# via
# -r requirements/base.txt
# asgiref
# black
# kombu
tzdata==2023.3
# via
# -r requirements/base.txt
# celery
# django-celery-beat
uritemplate==4.1.1
# via
# -r requirements/base.txt
# drf-yasg
urllib3==2.0.3
# via
# -r requirements/base.txt
# requests
vine==5.0.0
# via
# -r requirements/base.txt
# amqp
# celery
# kombu
wcwidth==0.2.6
# via
# -r requirements/base.txt
# ftfy
# prompt-toolkit
webencodings==0.5.1
# via
# -r requirements/base.txt
# bleach
# The following packages are considered to be unsafe in a requirements file:
# setuptools

View file

@ -5,7 +5,7 @@ import sys
def main():
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsreader.conf.dev")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsreader.conf.docker")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:

View file

@ -8,7 +8,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [

View file

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [("accounts", "0001_initial")]
operations = [migrations.RemoveField(model_name="user", name="username")]

View file

@ -6,7 +6,6 @@ import newsreader.accounts.models
class Migration(migrations.Migration):
dependencies = [("accounts", "0002_remove_user_username")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("accounts", "0003_auto_20190714_1417")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [("accounts", "0004_auto_20190714_1501")]
operations = [migrations.RemoveField(model_name="user", name="task_interval")]

View file

@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("accounts", "0005_remove_user_task_interval")]
operations = [

View file

@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("accounts", "0006_auto_20191116_1253")]
operations = [

View file

@ -15,7 +15,6 @@ def update_task_name(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [("accounts", "0007_auto_20191116_1255")]
operations = [migrations.RunPython(update_task_name)]

View file

@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("django_celery_beat", "0012_periodictask_expire_seconds"),
("accounts", "0008_auto_20200422_2243"),

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("accounts", "0009_auto_20200524_1218")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("accounts", "0010_auto_20200603_2230")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [("accounts", "0011_auto_20200913_2101")]
operations = [migrations.RemoveField(model_name="user", name="task")]

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("accounts", "0012_remove_user_task")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("accounts", "0013_user_auto_mark_read")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [("accounts", "0014_auto_20201218_2216")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("accounts", "0015_auto_20201219_1330")]
operations = [

View file

@ -1,11 +1,6 @@
from django.contrib.auth import views as django_views
from django.urls import reverse_lazy
from newsreader.news.collection.reddit import (
get_reddit_access_token,
get_reddit_authorization_url,
)
# PasswordResetView sends the mail
# PasswordResetDoneView shows a success message for the above

View file

@ -4,11 +4,6 @@ from django.views.generic import TemplateView
from registration.backends.default import views as registration_views
from newsreader.news.collection.reddit import (
get_reddit_access_token,
get_reddit_authorization_url,
)
# RegistrationView shows a registration form and sends the email
# RegistrationCompleteView shows after filling in the registration form

View file

@ -4,10 +4,6 @@ from django.views.generic.edit import FormView, ModelFormMixin
from newsreader.accounts.forms import UserSettingsForm
from newsreader.accounts.models import User
from newsreader.news.collection.reddit import (
get_reddit_access_token,
get_reddit_authorization_url,
)
class SettingsView(ModelFormMixin, FormView):

View file

@ -3,7 +3,7 @@ import os
from celery import Celery
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsreader.conf.dev")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "newsreader.conf.docker")
app = Celery("newsreader")
app.config_from_object("django.conf:settings", namespace="CELERY")

View file

@ -21,7 +21,7 @@ DJANGO_PROJECT_DIR = os.path.join(BASE_DIR, "src", "newsreader")
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
# SECURITY WARNING: don"t run with debug turned on in production!
DEBUG = True
DEBUG = False
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]
INTERNAL_IPS = ["127.0.0.1", "localhost"]
@ -50,6 +50,8 @@ INSTALLED_APPS = [
"newsreader.news.collection",
]
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
AUTHENTICATION_BACKENDS = [
"axes.backends.AxesBackend",
"django.contrib.auth.backends.ModelBackend",
@ -93,10 +95,11 @@ WSGI_APPLICATION = "newsreader.wsgi.application"
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"HOST": os.environ.get("POSTGRES_HOST", ""),
"NAME": os.environ.get("POSTGRES_NAME", "newsreader"),
"USER": os.environ.get("POSTGRES_USER"),
"PASSWORD": os.environ.get("POSTGRES_PASSWORD"),
"HOST": os.environ["POSTGRES_HOST"],
"PORT": os.environ["POSTGRES_PORT"],
"NAME": os.environ["POSTGRES_DB"],
"USER": os.environ["POSTGRES_USER"],
"PASSWORD": os.environ["POSTGRES_PASSWORD"],
}
}
@ -105,11 +108,11 @@ DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"LOCATION": "localhost:11211",
"LOCATION": "memcached:11211",
},
"axes": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"LOCATION": "localhost:11211",
"LOCATION": "memcached:11211",
},
}
@ -128,43 +131,43 @@ LOGGING = {
"format": "[{server_time}] {message}",
"style": "{",
},
"syslog": {
"class": "logging.Formatter",
"format": "[newsreader] {message}",
"style": "{",
},
},
"handlers": {
"console": {
"level": "INFO",
"filters": ["require_debug_true"],
"class": "logging.StreamHandler",
"formatter": "timestamped",
},
"file": {
"level": "DEBUG",
"class": "logging.handlers.RotatingFileHandler",
"filename": BASE_DIR / "logs" / "newsreader.log",
"backupCount": 5,
"maxBytes": 50000000, # 50 mB
"formatter": "timestamped",
},
"celery": {
"level": "INFO",
"filters": ["require_debug_false"],
"class": "logging.handlers.SysLogHandler",
"formatter": "syslog",
"address": "/dev/log",
},
"syslog": {
"level": "ERROR",
"filters": ["require_debug_false"],
"class": "logging.handlers.SysLogHandler",
"formatter": "syslog",
"address": "/dev/log",
"class": "logging.handlers.RotatingFileHandler",
"filename": BASE_DIR / "logs" / "celery.log",
"backupCount": 5,
"maxBytes": 50000000, # 50 mB
"formatter": "timestamped",
},
},
"loggers": {
"django": {"handlers": ["console", "syslog"], "level": "INFO"},
"django": {"handlers": ["console"], "level": "INFO"},
"django.server": {
"handlers": ["console", "syslog"],
"handlers": ["console"],
"level": "INFO",
"propagate": False,
},
"celery": {"handlers": ["celery", "console"], "level": "INFO"},
"newsreader": {"handlers": ["syslog", "console"], "level": "INFO"},
"celery.task": {"handlers": ["console", "celery"], "level": "INFO"},
"newsreader": {
"handlers": ["console", "file"],
"level": "DEBUG",
"propagate": False,
},
},
}
@ -208,9 +211,6 @@ STATICFILES_FINDERS = [
# Email
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
# Project settings
ENVIRONMENT = "development"
# Reddit integration
REDDIT_CLIENT_ID = "CLIENT_ID"
REDDIT_CLIENT_SECRET = "CLIENT_SECRET"
@ -251,7 +251,9 @@ SWAGGER_SETTINGS = {
# Celery
# https://docs.celeryproject.org/en/stable/userguide/configuration.html
# Note that celery settings are prefix with CELERY. See src/newsreader/celery.py.
CELERY_WORKER_HIJACK_ROOT_LOGGER = False
CELERY_BROKER_URL = "amqp://guest@rabbitmq:5672"
REGISTRATION_OPEN = True
REGISTRATION_AUTO_LOGIN = True
@ -261,7 +263,6 @@ ACCOUNT_ACTIVATION_DAYS = 7
SENTRY_CONFIG = {
"dsn": os.environ.get("SENTRY_DSN"),
"send_default_pii": False,
"environment": ENVIRONMENT,
"integrations": [DjangoIntegration(), CeleryIntegration()]
if DjangoIntegration and CeleryIntegration
else [],

View file

@ -18,11 +18,11 @@ AXES_FAILURE_LIMIT = 50
AXES_COOLOFF_TIME = None
try:
from .local import * # noqa
# Optionally use sentry integration
from sentry_sdk import init as sentry_init
from .local import * # noqa
SENTRY_CONFIG.update({"release": VERSION})
sentry_init(**SENTRY_CONFIG)

View file

@ -2,34 +2,21 @@ from .base import * # isort:skip
from .version import get_current_version
SECRET_KEY = "=q(ztyo)b6noom#a164g&s9vcj1aawa^g#ing_ir99=_zl4g&$"
ALLOWED_HOSTS = ["django", "127.0.0.1"]
INSTALLED_APPS += ["debug_toolbar", "django_extensions"]
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
LOGGING["loggers"].update(
{
"celery.task": {"handlers": ["console", "celery"], "level": "DEBUG"},
}
)
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "newsreader",
"USER": "newsreader",
"PASSWORD": "newsreader",
"HOST": "db",
}
}
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"LOCATION": "memcached:11211",
},
"axes": {
"BACKEND": "django.core.cache.backends.memcached.MemcachedCache",
"LOCATION": "memcached:11211",
},
}
DEBUG = True
# Project settings
VERSION = get_current_version()
@ -40,16 +27,12 @@ ENVIRONMENT = "docker"
AXES_FAILURE_LIMIT = 50
AXES_COOLOFF_TIME = None
# Celery
# https://docs.celeryproject.org/en/latest/userguide/configuration.html
CELERY_BROKER_URL = "amqp://guest:guest@rabbitmq:5672//"
try:
from .local import * # noqa
# Optionally use sentry integration
from sentry_sdk import init as sentry_init
from .local import * # noqa
SENTRY_CONFIG.update({"release": VERSION, "environment": ENVIRONMENT})
sentry_init(**SENTRY_CONFIG)

View file

@ -2,7 +2,16 @@ from .base import * # isort:skip
from .version import get_current_version
SECRET_KEY = "29%lkw+&n%^w4k#@_db2mo%*tc&xzb)x7xuq*(0$eucii%4r0c"
del LOGGING["handlers"]["file"]
del LOGGING["handlers"]["celery"]
LOGGING["loggers"].update(
{
"celery.task": {"handlers": ["console"], "level": "DEBUG"},
"newsreader": {"handlers": ["console"], "level": "INFO"},
}
)
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"

View file

@ -7,26 +7,13 @@ from .base import * # isort:skip
DEBUG = False
ALLOWED_HOSTS = ["rss.fudiggity.nl"]
ALLOWED_HOSTS = ["127.0.0.1", "localhost", "rss.fudiggity.nl", "django"]
ADMINS = [
("", email)
for email in os.getenv("ADMINS", "").split(",")
if os.environ.get("ADMINS")
]
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"HOST": os.environ["POSTGRES_HOST"],
"PORT": os.environ["POSTGRES_PORT"],
"NAME": os.environ["POSTGRES_NAME"],
"USER": os.environ["POSTGRES_USER"],
"PASSWORD": os.environ["POSTGRES_PASSWORD"],
}
}
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,8 @@
function isCSSVariablesSupported() {
const isCSSVariablesSupported = () => {
return window.CSS && window.CSS.supports('color', 'var(--fake-color');
}
};
function changeTheme(e) {
const changeTheme = event => {
const currentPref = sessionStorage.getItem('t-dark');
const isDark = currentPref && currentPref === 'true' ? true : false;
@ -14,12 +14,12 @@ function changeTheme(e) {
try {
sessionStorage.setItem('t-dark', !isDark);
} catch (e) {
} catch (error) {
// do nothing.
}
}
};
function prefersDarkTheme() {
const getThemePreference = () => {
try {
const currentPref = sessionStorage.getItem('t-dark');
@ -33,12 +33,12 @@ function prefersDarkTheme() {
} else {
return false;
}
} catch (e) {
} catch (error) {
return false;
}
}
};
function toggleDarkTheme(isDark) {
const toggleDarkTheme = isDark => {
if (isDark) {
document.documentElement.classList.add('dark-theme');
} else {
@ -47,30 +47,30 @@ function toggleDarkTheme(isDark) {
try {
sessionStorage.setItem('t-dark', isDark);
} catch (e) {
} catch (error) {
// do nothing.
}
}
};
function initThemeSelector() {
const initThemeSelector = () => {
const themeButton = document.getElementsByClassName('theme-switcher')[0];
const mqPrefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)');
const prefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)');
if (prefersDarkTheme()) {
if (getThemePreference()) {
toggleDarkTheme(true);
}
themeButton.addEventListener('click', changeTheme);
mqPrefersDarkTheme.addListener(mq => {
toggleDarkTheme(mq.matches);
prefersDarkTheme.addListener(mediaQuery => {
toggleDarkTheme(mediaQuery.matches);
});
}
};
function init() {
const init = () => {
if (isCSSVariablesSupported()) {
initThemeSelector();
}
}
};
init();

View file

@ -5,6 +5,7 @@ import { isEqual } from 'lodash';
import { fetchCategories } from './actions/categories';
import ScrollTop from './components/ScrollTop.js';
import Sidebar from './components/sidebar/Sidebar.js';
import PostList from './components/postlist/PostList.js';
import PostModal from './components/PostModal.js';
@ -41,6 +42,8 @@ class App extends React.Component {
/>
)}
<ScrollTop />
{this.props.error && (
<Messages messages={[{ type: 'error', text: this.props.error.message }]} />
)}

View file

@ -0,0 +1,40 @@
import React from 'react';
export default class ScrollTop extends React.Component {
scrollListener = ::this.scrollListener;
state = { showTop: false, showBottom: false };
componentDidMount() {
window.addEventListener('scroll', this.scrollListener);
}
scrollListener() {
const showBottom = window.innerHeight + window.scrollY < document.body.offsetHeight;
this.setState({
showTop: window.pageYOffset > 0 ? true : false,
showBottom: showBottom,
});
}
render() {
return (
<div className="scroll-to-top">
{this.state.showTop && (
<i
className="scroll-to-top__icon scroll-to-top__icon--top"
onClick={() => window.scrollTo(0, 0)}
/>
)}
{this.state.showBottom && (
<i
className="scroll-to-top__icon scroll-to-top__icon--bottom"
onClick={() => window.scrollTo(0, document.body.scrollHeight)}
/>
)}
</div>
);
}
}

View file

@ -21,13 +21,10 @@ class PostList extends React.Component {
}
checkScrollHeight(e) {
const currentHeight = window.scrollY + window.innerHeight;
const totalHeight = document.body.offsetHeight;
const currentPercentage = (currentHeight / totalHeight) * 100;
const postList = document.body.querySelector('.posts__list');
if (this.props.next && !this.props.lastReached) {
if (currentPercentage > 60 && !this.props.isFetching) {
if (window.scrollY + window.innerHeight >= postList.offsetHeight) {
this.paginate();
}
}

View file

@ -7,11 +7,7 @@ from rest_framework.generics import (
)
from rest_framework.response import Response
from newsreader.core.pagination import (
CursorPagination,
LargeResultSetPagination,
ResultSetPagination,
)
from newsreader.core.pagination import CursorPagination
from newsreader.news.collection.models import CollectionRule
from newsreader.news.collection.serializers import RuleSerializer
from newsreader.news.core.filters import ReadFilter

View file

@ -18,7 +18,6 @@ from newsreader.news.collection.base import (
)
from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.exceptions import (
StreamDeniedException,
StreamException,
StreamNotFoundException,
StreamParseException,

View file

@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []

View file

@ -7,7 +7,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("collection", "0002_auto_20190714_1036")]
operations = [

View file

@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("collection", "0003_auto_20190714_1417")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("collection", "0004_auto_20190714_1422")]
operations = [

View file

@ -7,7 +7,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("collection", "0005_auto_20200303_1932"),

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("collection", "0006_auto_20200412_1955")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("collection", "0007_collectionrule_enabled")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("collection", "0008_collectionrule_type")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("collection", "0009_auto_20200807_2030")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [("collection", "0010_auto_20200913_2101")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("collection", "0011_auto_20200913_2157")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("collection", "0012_auto_20201219_1331")]
operations = [

View file

@ -12,7 +12,6 @@ def reset_default_downvotes(apps, schema_editor):
class Migration(migrations.Migration):
dependencies = [("collection", "0013_auto_20201219_1345")]
operations = [migrations.RunPython(reset_default_downvotes)]

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("collection", "0014_auto_20201219_1346")]
operations = [

View file

@ -23,10 +23,6 @@ from newsreader.news.collection.base import (
Scheduler,
)
from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.constants import (
WHITELISTED_ATTRIBUTES,
WHITELISTED_TAGS,
)
from newsreader.news.collection.exceptions import (
BuilderDuplicateException,
BuilderException,

View file

@ -6,7 +6,6 @@ from bs4 import BeautifulSoup
from newsreader.news.collection.exceptions import (
StreamDeniedException,
StreamException,
StreamForbiddenException,
StreamNotFoundException,
StreamParseException,

View file

@ -11,10 +11,8 @@ from freezegun import freeze_time
from newsreader.news.collection.exceptions import (
StreamDeniedException,
StreamException,
StreamForbiddenException,
StreamNotFoundException,
StreamParseException,
StreamTimeOutException,
)
from newsreader.news.collection.feed import FeedCollector

View file

@ -206,6 +206,7 @@ simple_mock_parsed = {
"updated": "Sun, 12 Jul 2020 17:21:20 GMT",
"updated_parsed": struct_time((2020, 7, 12, 17, 21, 20, 6, 194, 0)),
},
"headers": {},
"namespaces": {
"": "http://www.w3.org/2005/Atom",
"content": "http://purl.org/rss/1.0/modules/content/",

View file

@ -193,3 +193,35 @@ class TwitterClientTestCase(TestCase):
self.assertIsNone(user.twitter_oauth_token)
self.assertIsNone(user.twitter_oauth_token_secret)
def test_client_does_not_reset_token(self):
"""
The user's token and refresh token should not be reset when an generic
exception is caught
"""
user = UserFactory(
twitter_oauth_token=str(uuid4()), twitter_oauth_token_secret=str(uuid4())
)
timeline = TwitterTimelineFactory(user=user)
response = Mock(json=lambda: {"errors": [{"code": 100}]})
self.mocked_read.side_effect = StreamException(
message="Generic message", response=response
)
with TwitterClient([timeline]) as client:
for data, stream in client:
with self.subTest(data=data, stream=stream):
self.assertIsNone(data)
self.assertIsNone(stream)
self.assertEquals(stream.rule.error, "")
self.assertEquals(stream.rule.succeeded, False)
self.mocked_read.assert_called()
user.refresh_from_db()
timeline.refresh_from_db()
self.assertIsNotNone(user.twitter_oauth_token)
self.assertIsNotNone(user.twitter_oauth_token_secret)

View file

@ -10,7 +10,6 @@ from newsreader.news.collection.exceptions import (
StreamNotFoundException,
StreamParseException,
StreamTimeOutException,
StreamTooManyException,
)
from newsreader.news.collection.tests.factories import TwitterTimelineFactory
from newsreader.news.collection.tests.twitter.stream.mocks import simple_mock

View file

@ -28,7 +28,6 @@ from newsreader.news.collection.exceptions import (
BuilderException,
BuilderMissingDataException,
BuilderParseException,
StreamDeniedException,
StreamException,
StreamNotFoundException,
StreamParseException,
@ -250,12 +249,14 @@ class TwitterClient(PostClient):
try:
response_data = e.response.json()
except JSONDecodeError:
logger.exception("Could not parse json for request")
continue
if "errors" in response_data:
errors = response_data["errors"]
token_expired = any(error["code"] == 89 for error in errors)
if token_expired:
try:
import sentry_sdk

View file

@ -6,11 +6,7 @@ from django.views.generic.edit import CreateView, FormView, UpdateView
from django_celery_beat.models import IntervalSchedule
from newsreader.news.collection.choices import RuleTypeChoices
from newsreader.news.collection.forms import (
CollectionRuleBulkForm,
FeedForm,
OPMLImportForm,
)
from newsreader.news.collection.forms import FeedForm, OPMLImportForm
from newsreader.news.collection.models import CollectionRule
from newsreader.news.collection.views.base import (
CollectionRuleDetailMixin,

View file

@ -6,10 +6,7 @@ from django.views.generic.edit import FormView
from django.views.generic.list import ListView
from newsreader.news.collection.forms import CollectionRuleBulkForm
from newsreader.news.collection.views.base import (
CollectionRuleDetailMixin,
CollectionRuleViewMixin,
)
from newsreader.news.collection.views.base import CollectionRuleViewMixin
class CollectionRuleListView(CollectionRuleViewMixin, ListView):

View file

@ -2,7 +2,6 @@ from rest_framework import status
from rest_framework.generics import (
GenericAPIView,
ListAPIView,
ListCreateAPIView,
RetrieveUpdateAPIView,
RetrieveUpdateDestroyAPIView,
get_object_or_404,

View file

@ -8,7 +8,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [

View file

@ -7,7 +7,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("core", "0001_initial")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("core", "0002_auto_20190714_1425")]
operations = [

View file

@ -5,7 +5,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("core", "0003_post_read"),

View file

@ -7,7 +7,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("core", "0004_auto_20191116_1315"),

View file

@ -6,7 +6,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("core", "0005_auto_20200412_1955")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("core", "0006_auto_20200524_1218")]
operations = [

View file

@ -4,7 +4,6 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("core", "0007_auto_20200706_2312")]
operations = [

View file

@ -14,7 +14,6 @@ from newsreader.news.core.views import (
CategoryCreateView,
CategoryListView,
CategoryUpdateView,
NewsView,
)

View file

@ -26,3 +26,4 @@
@import './post-message/index';
@import './posts/index';
@import './posts-info/index';
@import './scroll-to-top/index';

View file

@ -5,4 +5,6 @@
padding: 0;
cursor: pointer;
z-index: 1000;
}

View file

@ -0,0 +1,33 @@
.scroll-to-top {
display: flex;
gap: 10px;
position: fixed;
right: 15%;
bottom: 0;
margin: 0 0 20px 0;
&:hover {
cursor: pointer;
}
&__icon {
font-style: initial;
padding: 10px;
background-color: var(--lightest-accent-color);
&--top:before {
@include font-awesome;
content: "\f062";
}
&--bottom:before {
@include font-awesome;
content: "\f063";
}
}
}

View file

@ -0,0 +1 @@
@import './scroll-to-top';

View file

@ -1,6 +1,6 @@
from django.core.cache import cache
from time import monotonic
from celery.five import monotonic
from django.core.cache import cache
LOCK_EXPIRE = 60 * 10 # 10 minutes