diff --git a/.editorconfig b/.editorconfig index faec82a..68a5db3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -39,7 +39,7 @@ indent_style = tab [docs/**.txt] max_line_length = 79 -[*.yml] +[*.yaml] indent_size = 2 [*.md] diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..9f2844f --- /dev/null +++ b/.flake8 @@ -0,0 +1,5 @@ +[flake8] +max-line-length = 120 +per-file-ignores = + later42/views/index.py: E722 + later42/views/reader.py: E722 diff --git a/.gitignore b/.gitignore index 3a3807a..50af51c 100644 --- a/.gitignore +++ b/.gitignore @@ -139,4 +139,4 @@ dmypy.json cython_debug/ db/*.sqlite3 -static/* \ No newline at end of file +static/* diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..1fc7557 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,27 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: check-added-large-files + - id: check-ast + - id: check-byte-order-marker + - id: check-case-conflict + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: check-yaml + args: [--allow-multiple-documents] + - id: debug-statements + - id: detect-private-key + - id: end-of-file-fixer + - id: requirements-txt-fixer + - id: trailing-whitespace + - repo: https://github.com/PyCQA/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + - repo: https://github.com/ambv/black + rev: stable + hooks: + - id: black + language_version: python3.11 diff --git a/Readme.md b/Readme.md index aba47ec..f90c259 100644 --- a/Readme.md +++ b/Readme.md @@ -29,7 +29,7 @@ SECRET_KEY | | The secret key to make hashes DOMAIN | localhost | Domain name where the site is hosted DB_TYPE | SQLite | Type of DB (SQLite, Postgres) DB_HOST | None | Host for Postgres DB -DB_NAME | None | Name for Postgres DB +DB_NAME | None | Name for Postgres DB DB_USER | None | User for Postgres DB DB_PASS | None | Password for Postgres DB REDIS_URL | redis://localhost:6379 | Connection string for Redis diff --git a/later42/__init__.py b/later42/__init__.py index fb989c4..53f4ccb 100644 --- a/later42/__init__.py +++ b/later42/__init__.py @@ -1,3 +1,3 @@ from .celery import app as celery_app -__all__ = ('celery_app',) +__all__ = ("celery_app",) diff --git a/later42/admin.py b/later42/admin.py index 68bab2b..fe26d71 100644 --- a/later42/admin.py +++ b/later42/admin.py @@ -2,4 +2,4 @@ from django.contrib import admin from .models.urls import URL -admin.site.register(URL) \ No newline at end of file +admin.site.register(URL) diff --git a/later42/asgi.py b/later42/asgi.py index 28892ab..a7b6270 100644 --- a/later42/asgi.py +++ b/later42/asgi.py @@ -11,6 +11,6 @@ import os from django.core.asgi import get_asgi_application -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'later42.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "later42.settings") application = get_asgi_application() diff --git a/later42/celery.py b/later42/celery.py index 3605c12..a4609ef 100644 --- a/later42/celery.py +++ b/later42/celery.py @@ -3,15 +3,15 @@ import os from celery import Celery # Set the default Django settings module for the 'celery' program. -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'later42.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "later42.settings") -app = Celery('later42') +app = Celery("later42") # Using a string here means the worker doesn't have to serialize # the configuration object to child processes. # - namespace='CELERY' means all celery-related configuration keys # should have a `CELERY_` prefix. -app.config_from_object('django.conf:settings', namespace='CELERY') +app.config_from_object("django.conf:settings", namespace="CELERY") # Load task modules from all registered Django apps. app.autodiscover_tasks() @@ -19,4 +19,4 @@ app.autodiscover_tasks() @app.task(bind=True) def debug_task(self): - print(f'Request: {self.request!r}') + print(f"Request: {self.request!r}") diff --git a/later42/forms.py b/later42/forms.py index 08c3669..b3ff97a 100644 --- a/later42/forms.py +++ b/later42/forms.py @@ -7,29 +7,24 @@ from django.contrib.auth.forms import UserCreationForm class SignUpForm(UserCreationForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['username'].widget.attrs.update( - {'class': 'form-control'} - ) - self.fields['password1'].widget.attrs.update( - {'class': 'form-control'} - ) - self.fields['password2'].widget.attrs.update( - {'class': 'form-control'} - ) - self.fields['username'].label = 'Логин' + self.fields["username"].widget.attrs.update({"class": "form-control"}) + self.fields["password1"].widget.attrs.update({"class": "form-control"}) + self.fields["password2"].widget.attrs.update({"class": "form-control"}) + self.fields["username"].label = "Логин" class Meta: model = User - fields = ('username', 'email', 'password1', 'password2',) + fields = ( + "username", + "email", + "password1", + "password2", + ) class CustomLoginForm(AuthenticationForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['username'].widget.attrs.update( - {'class': 'form-control'} - ) - self.fields['password'].widget.attrs.update( - {'class': 'form-control'} - ) - self.fields['username'].label = 'Логин' + self.fields["username"].widget.attrs.update({"class": "form-control"}) + self.fields["password"].widget.attrs.update({"class": "form-control"}) + self.fields["username"].label = "Логин" diff --git a/later42/libs/content.py b/later42/libs/content.py index 6b16c1f..234caa8 100644 --- a/later42/libs/content.py +++ b/later42/libs/content.py @@ -3,10 +3,10 @@ from newspaper import Article, Config def sanitize_img_size(html: str): - soup = BeautifulSoup(html, 'html.parser') - for img in soup.find_all('img'): - img['width'] = '100%' - img['height'] = 'auto' + soup = BeautifulSoup(html, "html.parser") + for img in soup.find_all("img"): + img["width"] = "100%" + img["height"] = "auto" return str(soup) diff --git a/later42/logging/debug.py b/later42/logging/debug.py index f931096..ea6f135 100644 --- a/later42/logging/debug.py +++ b/later42/logging/debug.py @@ -1,10 +1,8 @@ class ExceptionLoggingMiddleware(object): - def __init__(self, get_response): self.get_response = get_response def __call__(self, request): - # Code to be executed for each request before # the view (and later middleware) are called. print(request.body) @@ -15,4 +13,4 @@ class ExceptionLoggingMiddleware(object): response = self.get_response(request) - return response \ No newline at end of file + return response diff --git a/later42/migrations/0001_initial.py b/later42/migrations/0001_initial.py index c4ecc08..f6b3cbe 100644 --- a/later42/migrations/0001_initial.py +++ b/later42/migrations/0001_initial.py @@ -6,7 +6,6 @@ import django.db.models.deletion class Migration(migrations.Migration): - initial = True dependencies = [ @@ -15,12 +14,23 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='URL', + name="URL", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)), - ('url', models.CharField(max_length=2000)), - ('title', models.CharField(max_length=2000)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False + ), + ), + ("url", models.CharField(max_length=2000)), + ("title", models.CharField(max_length=2000)), + ( + "user", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), ], ), ] diff --git a/later42/migrations/0002_url_archived.py b/later42/migrations/0002_url_archived.py index 0da94ff..247ffab 100644 --- a/later42/migrations/0002_url_archived.py +++ b/later42/migrations/0002_url_archived.py @@ -4,15 +4,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('later42', '0001_initial'), + ("later42", "0001_initial"), ] operations = [ migrations.AddField( - model_name='url', - name='archived', + model_name="url", + name="archived", field=models.BooleanField(default=False), ), ] diff --git a/later42/migrations/0003_url_content.py b/later42/migrations/0003_url_content.py index 834eb10..a24e6e7 100644 --- a/later42/migrations/0003_url_content.py +++ b/later42/migrations/0003_url_content.py @@ -4,15 +4,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('later42', '0002_url_archived'), + ("later42", "0002_url_archived"), ] operations = [ migrations.AddField( - model_name='url', - name='content', + model_name="url", + name="content", field=models.TextField(blank=True, null=True), ), ] diff --git a/later42/migrations/0004_articles.py b/later42/migrations/0004_articles.py index b65e97b..752def1 100644 --- a/later42/migrations/0004_articles.py +++ b/later42/migrations/0004_articles.py @@ -5,18 +5,27 @@ import django.db.models.deletion class Migration(migrations.Migration): - dependencies = [ - ('later42', '0003_url_content'), + ("later42", "0003_url_content"), ] operations = [ migrations.CreateModel( - name='Articles', + name="Articles", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)), - ('content', models.TextField(blank=True, null=True)), - ('url', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='later42.url')), + ( + "id", + models.AutoField( + auto_created=True, primary_key=True, serialize=False + ), + ), + ("content", models.TextField(blank=True, null=True)), + ( + "url", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="later42.url" + ), + ), ], ), ] diff --git a/later42/migrations/0005_rename_articles_article.py b/later42/migrations/0005_rename_articles_article.py index 6bc258b..93794f3 100644 --- a/later42/migrations/0005_rename_articles_article.py +++ b/later42/migrations/0005_rename_articles_article.py @@ -4,14 +4,13 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('later42', '0004_articles'), + ("later42", "0004_articles"), ] operations = [ migrations.RenameModel( - old_name='Articles', - new_name='Article', + old_name="Articles", + new_name="Article", ), ] diff --git a/later42/migrations/0006_remove_url_content_remove_url_title_article_short_and_more.py b/later42/migrations/0006_remove_url_content_remove_url_title_article_short_and_more.py index 251ee97..166ea17 100644 --- a/later42/migrations/0006_remove_url_content_remove_url_title_article_short_and_more.py +++ b/later42/migrations/0006_remove_url_content_remove_url_title_article_short_and_more.py @@ -4,28 +4,27 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('later42', '0005_rename_articles_article'), + ("later42", "0005_rename_articles_article"), ] operations = [ migrations.RemoveField( - model_name='url', - name='content', + model_name="url", + name="content", ), migrations.RemoveField( - model_name='url', - name='title', + model_name="url", + name="title", ), migrations.AddField( - model_name='article', - name='short', + model_name="article", + name="short", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='article', - name='title', + model_name="article", + name="title", field=models.CharField(blank=True, max_length=2000, null=True), ), ] diff --git a/later42/migrations/0007_article_img.py b/later42/migrations/0007_article_img.py index edc8703..144a978 100644 --- a/later42/migrations/0007_article_img.py +++ b/later42/migrations/0007_article_img.py @@ -4,15 +4,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('later42', '0006_remove_url_content_remove_url_title_article_short_and_more'), + ("later42", "0006_remove_url_content_remove_url_title_article_short_and_more"), ] operations = [ migrations.AddField( - model_name='article', - name='img', + model_name="article", + name="img", field=models.URLField(blank=True, null=True), ), ] diff --git a/later42/models/urls.py b/later42/models/urls.py index c7c962b..b0421a7 100644 --- a/later42/models/urls.py +++ b/later42/models/urls.py @@ -1,7 +1,7 @@ from django.contrib.auth.models import User from django.db import models -User._meta.get_field('email')._unique = True +User._meta.get_field("email")._unique = True class URL(models.Model): diff --git a/later42/serializers.py b/later42/serializers.py index 695f76b..97eac34 100644 --- a/later42/serializers.py +++ b/later42/serializers.py @@ -5,10 +5,10 @@ from rest_framework import serializers class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User - fields = ['url', 'username', 'email', 'groups'] + fields = ["url", "username", "email", "groups"] class GroupSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Group - fields = ['url', 'name'] \ No newline at end of file + fields = ["url", "name"] diff --git a/later42/settings.py b/later42/settings.py index 61d1c2e..2e7c15a 100644 --- a/later42/settings.py +++ b/later42/settings.py @@ -21,80 +21,81 @@ BASE_DIR = Path(__file__).resolve().parent.parent # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = os.getenv( - 'SECRET', 'django-insecure-c%g@wujt4dco#e%k-!25o3)0%t+wm5=k%l(m!kk^p_g%kknod!') + "SECRET", "django-insecure-c%g@wujt4dco#e%k-!25o3)0%t+wm5=k%l(m!kk^p_g%kknod!" +) # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = os.getenv('DEBUG', False) +DEBUG = os.getenv("DEBUG", False) -ALLOWED_HOSTS = ['*'] -CSRF_TRUSTED_ORIGINS = ['https://' + os.getenv('DOMAIN', 'localhost')] +ALLOWED_HOSTS = ["*"] +CSRF_TRUSTED_ORIGINS = ["https://" + os.getenv("DOMAIN", "localhost")] # Application definition INSTALLED_APPS = [ - 'later42', - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'rest_framework', - 'rest_framework.authtoken', + "later42", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "rest_framework", + "rest_framework.authtoken", ] MIDDLEWARE = [ # 'later42.logging.debug.ExceptionLoggingMiddleware', - 'django.middleware.security.SecurityMiddleware', - 'whitenoise.middleware.WhiteNoiseMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', + "django.middleware.security.SecurityMiddleware", + "whitenoise.middleware.WhiteNoiseMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", ] -ROOT_URLCONF = 'later42.urls' +ROOT_URLCONF = "later42.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", ], }, }, ] -WSGI_APPLICATION = 'later42.wsgi.application' +WSGI_APPLICATION = "later42.wsgi.application" # Database # https://docs.djangoproject.com/en/4.1/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db/db.sqlite3', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db/db.sqlite3", } } -if os.getenv('DB_TYPE', 'SQLite') == 'postgres': +if os.getenv("DB_TYPE", "SQLite") == "postgres": DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'HOST': os.environ.get('DB_HOST', None), - 'NAME': os.environ.get('DB_NAME', None), - 'USER': os.environ.get('DB_USER', None), - 'PASSWORD': os.environ.get('DB_PASS', None), + "default": { + "ENGINE": "django.db.backends.postgresql", + "HOST": os.environ.get("DB_HOST", None), + "NAME": os.environ.get("DB_NAME", None), + "USER": os.environ.get("DB_USER", None), + "PASSWORD": os.environ.get("DB_PASS", None), } } @@ -104,16 +105,16 @@ if os.getenv('DB_TYPE', 'SQLite') == 'postgres': AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] @@ -121,9 +122,9 @@ AUTH_PASSWORD_VALIDATORS = [ # Internationalization # https://docs.djangoproject.com/en/4.1/topics/i18n/ -LANGUAGE_CODE = 'ru-ru' +LANGUAGE_CODE = "ru-ru" -TIME_ZONE = os.getenv('TZ', 'UTC') +TIME_ZONE = os.getenv("TZ", "UTC") USE_I18N = True @@ -133,7 +134,7 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.1/howto/static-files/ -STATIC_URL = 'static/' +STATIC_URL = "static/" STATICFILES_DIRS = [ BASE_DIR / "later42/static", ] @@ -143,70 +144,68 @@ STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage" # Default primary key field type # https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" LOGIN_REDIRECT_URL = "index" LOGOUT_REDIRECT_URL = "index" REST_FRAMEWORK = { - 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', - 'PAGE_SIZE': 10, - 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework.authentication.TokenAuthentication', - ), - 'DEFAULT_PERMISSION_CLASSES': ( - 'rest_framework.permissions.IsAuthenticated', + "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination", + "PAGE_SIZE": 10, + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework.authentication.TokenAuthentication", ), + "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",), } # Celery Configuration Options CELERY_TIMEZONE = TIME_ZONE CELERY_TASK_TRACK_STARTED = True CELERY_TASK_TIME_LIMIT = 30 * 60 -CELERY_BROKER_URL = os.getenv('REDIS_URL', 'redis://localhost:6379') -CELERY_RESULT_BACKEND = os.getenv('REDIS_URL', 'redis://localhost:6379') +CELERY_BROKER_URL = os.getenv("REDIS_URL", "redis://localhost:6379") +CELERY_RESULT_BACKEND = os.getenv("REDIS_URL", "redis://localhost:6379") URLS_PER_PAGE = 20 -READABILITY_HOST = os.getenv('READABILITY_HOST', "http://localhost:8080/") +READABILITY_HOST = os.getenv("READABILITY_HOST", "http://localhost:8080/") -EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' +EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend" if DEBUG: - EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' -EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS', False) -EMAIL_HOST = os.getenv('EMAIL_HOST', 'smtp') -EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', None) -EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', None) -EMAIL_PORT = os.getenv('EMAIL_PORT', '25') -EMAIL_FROM = os.getenv('EMAIL_FROM', 'noreply@later42.com') + EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" +EMAIL_USE_TLS = os.getenv("EMAIL_USE_TLS", False) +EMAIL_HOST = os.getenv("EMAIL_HOST", "smtp") +EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", None) +EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", None) +EMAIL_PORT = os.getenv("EMAIL_PORT", "25") +EMAIL_FROM = os.getenv("EMAIL_FROM", "noreply@later42.com") AIRBRAKE = dict( - project_id=os.getenv('AIRBRAKE_PROJECT_ID', None), - project_key=os.getenv('AIRBRAKE_PROJECT_KEY', None), - environment=os.getenv('AIRBRAKE_ENVIRONMENT', 'development'), + project_id=os.getenv("AIRBRAKE_PROJECT_ID", None), + project_key=os.getenv("AIRBRAKE_PROJECT_KEY", None), + environment=os.getenv("AIRBRAKE_ENVIRONMENT", "development"), ) -if AIRBRAKE['project_id'] is not None and AIRBRAKE['project_key'] is not None: - MIDDLEWARE += ['pybrake.middleware.django.AirbrakeMiddleware'] +if AIRBRAKE["project_id"] is not None and AIRBRAKE["project_key"] is not None: + MIDDLEWARE += ["pybrake.middleware.django.AirbrakeMiddleware"] LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'handlers': { - 'airbrake': { - 'level': 'ERROR', - 'class': 'pybrake.LoggingHandler', + "version": 1, + "disable_existing_loggers": False, + "handlers": { + "airbrake": { + "level": "ERROR", + "class": "pybrake.LoggingHandler", }, }, - 'loggers': { - 'app': { - 'handlers': ['airbrake'], - 'level': 'ERROR', - 'propagate': True, + "loggers": { + "app": { + "handlers": ["airbrake"], + "level": "ERROR", + "propagate": True, }, }, } -SENTRY_DSN = os.getenv('SENTRY_DSN', None) +SENTRY_DSN = os.getenv("SENTRY_DSN", None) if SENTRY_DSN is not None: import sentry_sdk diff --git a/later42/static/assets/site.webmanifest b/later42/static/assets/site.webmanifest index 45dc8a2..1dd9112 100644 --- a/later42/static/assets/site.webmanifest +++ b/later42/static/assets/site.webmanifest @@ -1 +1 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} diff --git a/later42/static/css/styles.css b/later42/static/css/styles.css index ccb24d7..990390a 100644 --- a/later42/static/css/styles.css +++ b/later42/static/css/styles.css @@ -11245,4 +11245,4 @@ header.masthead .site-heading .h1 { .post-preview > a > .post-title { font-size: 2.25rem; } -} \ No newline at end of file +} diff --git a/later42/tasks.py b/later42/tasks.py index 6abb604..4e2cea8 100644 --- a/later42/tasks.py +++ b/later42/tasks.py @@ -13,7 +13,11 @@ AIRBRAKE_PROJECT_ID = os.getenv("AIRBRAKE_PROJECT_ID", None) AIRBRAKE_PROJECT_KEY = os.getenv("AIRBRAKE_PROJECT_KEY", None) if AIRBRAKE_PROJECT_ID is not None and AIRBRAKE_PROJECT_KEY is not None: - notifier = pybrake.Notifier(project_id=AIRBRAKE_PROJECT_ID, project_key=AIRBRAKE_PROJECT_KEY, environment="celery") + notifier = pybrake.Notifier( + project_id=AIRBRAKE_PROJECT_ID, + project_key=AIRBRAKE_PROJECT_KEY, + environment="celery", + ) patch_celery(notifier) diff --git a/later42/templates/reader.html b/later42/templates/reader.html index bc8a7d9..df62576 100644 --- a/later42/templates/reader.html +++ b/later42/templates/reader.html @@ -32,4 +32,4 @@ Открыть оригинал ссылки. {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/later42/templates/registration/email_activation.html b/later42/templates/registration/email_activation.html index 986d3bf..5d6ff1a 100644 --- a/later42/templates/registration/email_activation.html +++ b/later42/templates/registration/email_activation.html @@ -3,4 +3,4 @@ Ссылка для активации аккаунта: https://{{ domain }}{% url 'activate' uidb64=uid token=token %} -{% endautoescape %} \ No newline at end of file +{% endautoescape %} diff --git a/later42/templates/registration/password_change_done.html b/later42/templates/registration/password_change_done.html index eb2eb63..403e671 100644 --- a/later42/templates/registration/password_change_done.html +++ b/later42/templates/registration/password_change_done.html @@ -3,4 +3,4 @@ {% block content %}
Пароль успешно изменен.
Перейти в Профиль.
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/later42/templates/registration/password_reset_complete.html b/later42/templates/registration/password_reset_complete.html index c58b876..3f7d5af 100644 --- a/later42/templates/registration/password_reset_complete.html +++ b/later42/templates/registration/password_reset_complete.html @@ -3,4 +3,4 @@ {% block content %}Пароль успешно изменен.
Вы можете войти.
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/later42/templates/registration/password_reset_confirm.html b/later42/templates/registration/password_reset_confirm.html index 8daf8bc..15e40e4 100644 --- a/later42/templates/registration/password_reset_confirm.html +++ b/later42/templates/registration/password_reset_confirm.html @@ -29,4 +29,4 @@Ссылка неверная или уже была использована.
Попробуйте сбросить пароль еще раз.
Сообщение успешно отправлено.
Вы должны получить его в скором времени.
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/later42/tests/views/test_api.py b/later42/tests/views/test_api.py index 4bd9620..03d26a2 100644 --- a/later42/tests/views/test_api.py +++ b/later42/tests/views/test_api.py @@ -1,25 +1,20 @@ from django.contrib.auth.models import User -from django.test import TestCase, Client +from django.test import Client, TestCase from django.urls import reverse from rest_framework.authtoken.models import Token from rest_framework.test import APIClient -from later42.models.urls import URL - class ApiTests(TestCase): def setUp(self) -> None: - self.username = 'testuser1' - self.email = 'testuser1@email.com' - self.password = 'password1234567QWERTY' - self.user = User.objects.create( - username=self.username, - email=self.email - ) + self.username = "testuser1" + self.email = "testuser1@email.com" + self.password = "password1234567QWERTY" + self.user = User.objects.create(username=self.username, email=self.email) self.user.set_password(self.password) self.user.save() - self.url = 'https://google.com' + self.url = "https://google.com" self.c = Client() self.c.login(username=self.username, password=self.password) @@ -29,7 +24,7 @@ class ApiTests(TestCase): def test_url_create(self): token = Token.objects.get(user=self.user) client = APIClient() - client.credentials(HTTP_AUTHORIZATION='Token ' + token.key) - response = client.post(reverse('urls') + f'?url={self.url}') + client.credentials(HTTP_AUTHORIZATION="Token " + token.key) + response = client.post(reverse("urls") + f"?url={self.url}") assert response.status_code == 200 - self.assertContains(response, 'success') + self.assertContains(response, "success") diff --git a/later42/tests/views/test_api_token.py b/later42/tests/views/test_api_token.py index c3943c9..7fb923c 100644 --- a/later42/tests/views/test_api_token.py +++ b/later42/tests/views/test_api_token.py @@ -6,24 +6,21 @@ from rest_framework.authtoken.models import Token class ApiKeyTests(TestCase): def setUp(self) -> None: - self.username = 'testuser1' - self.email = 'testuser1@email.com' - self.password = 'password1234567QWERTY' - self.user = User.objects.create( - username=self.username, - email=self.email - ) + self.username = "testuser1" + self.email = "testuser1@email.com" + self.password = "password1234567QWERTY" + self.user = User.objects.create(username=self.username, email=self.email) self.user.set_password(self.password) self.user.save() self.c = Client() self.c.login(username=self.username, password=self.password) self.assertTrue(self.user.is_authenticated) - self.response = self.c.get(reverse('api_token')) + self.response = self.c.get(reverse("api_token")) def test_api_key_creation(self): assert self.response.status_code == 302 - assert self.response.url == '/profile/' + assert self.response.url == "/profile/" def test_api_key_created(self): token = Token.objects.get(user=self.user) @@ -31,6 +28,6 @@ class ApiKeyTests(TestCase): def test_api_key_reset(self): token_old = Token.objects.get(user=self.user) - self.response = self.c.get(reverse('api_token')) + self.response = self.c.get(reverse("api_token")) token_new = Token.objects.get(user=self.user) assert token_old != token_new diff --git a/later42/tests/views/test_index.py b/later42/tests/views/test_index.py index 727cebd..353d6d1 100644 --- a/later42/tests/views/test_index.py +++ b/later42/tests/views/test_index.py @@ -1,20 +1,16 @@ from django.contrib.auth.models import User -from django.test import TestCase, Client +from django.test import Client, TestCase from django.urls import reverse -from rest_framework.authtoken.models import Token from later42.models.urls import URL class IndexTests(TestCase): def setUp(self) -> None: - self.username = 'testuser1' - self.email = 'testuser1@email.com' - self.password = 'password1234567QWERTY' - self.user = User.objects.create( - username=self.username, - email=self.email - ) + self.username = "testuser1" + self.email = "testuser1@email.com" + self.password = "password1234567QWERTY" + self.user = User.objects.create(username=self.username, email=self.email) self.user.set_password(self.password) self.user.save() @@ -23,11 +19,8 @@ class IndexTests(TestCase): self.assertTrue(self.user.is_authenticated) def test_url_delete(self): - url = URL.objects.create( - url='https://www.google.com/', - user=self.user - ) + url = URL.objects.create(url="https://www.google.com/", user=self.user) url.save() - self.c.delete(reverse('delete', kwargs={'url_id': url.id})) + self.c.delete(reverse("delete", kwargs={"url_id": url.id})) url = URL.objects.filter(id=url.id) assert len(url) == 0 diff --git a/later42/tests/views/test_login.py b/later42/tests/views/test_login.py index 4314c1e..804ac60 100644 --- a/later42/tests/views/test_login.py +++ b/later42/tests/views/test_login.py @@ -5,13 +5,10 @@ from django.urls import reverse class LoginPageTests(TestCase): def setUp(self) -> None: - self.username = 'testuser1' - self.email = 'testuser1@email.com' - self.password = 'password1234567QWERTY' - self.user = User.objects.create( - username=self.username, - email=self.email - ) + self.username = "testuser1" + self.email = "testuser1@email.com" + self.password = "password1234567QWERTY" + self.user = User.objects.create(username=self.username, email=self.email) self.user.set_password(self.password) self.user.save() @@ -20,22 +17,22 @@ class LoginPageTests(TestCase): self.assertTrue(self.user.is_authenticated) def test_login_page(self): - response = self.client.get(reverse('login')) + response = self.client.get(reverse("login")) assert response.status_code == 200 def test_index_page_after_login(self): - response = self.c.get(reverse('index')) + response = self.c.get(reverse("index")) assert response.status_code == 200 - assert response.context['user'].is_authenticated - self.assertTemplateUsed(response, 'index.html') - self.assertContains(response, '/accounts/logout/') + assert response.context["user"].is_authenticated + self.assertTemplateUsed(response, "index.html") + self.assertContains(response, "/accounts/logout/") def test_profile_page_template(self): - response = self.c.get(reverse('profile')) + response = self.c.get(reverse("profile")) assert response.status_code == 200 - self.assertTemplateUsed(response, 'profile.html') + self.assertTemplateUsed(response, "profile.html") def test_api_key_creation_button(self): - response = self.c.get(reverse('profile')) + response = self.c.get(reverse("profile")) assert response.status_code == 200 self.assertContains(response, 'id="createbutton"') diff --git a/later42/tests/views/test_pages.py b/later42/tests/views/test_pages.py index 966ec07..7cff274 100644 --- a/later42/tests/views/test_pages.py +++ b/later42/tests/views/test_pages.py @@ -5,15 +5,15 @@ from django.urls import reverse class PageTests(TestCase): def test_index_page_url(self): - response = self.client.get(reverse('index')) + response = self.client.get(reverse("index")) self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, template_name='index.html') + self.assertTemplateUsed(response, template_name="index.html") def test_about_page_url(self): - response = self.client.get(reverse('about')) + response = self.client.get(reverse("about")) self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, template_name='about.html') + self.assertTemplateUsed(response, template_name="about.html") def test_profile_page_url_redirect(self): - response = self.client.get(reverse('profile')) + response = self.client.get(reverse("profile")) self.assertEqual(response.status_code, 302) diff --git a/later42/tests/views/test_signup.py b/later42/tests/views/test_signup.py index 01f8426..4b2f513 100644 --- a/later42/tests/views/test_signup.py +++ b/later42/tests/views/test_signup.py @@ -6,27 +6,30 @@ from django.urls import reverse class SignUpPageTests(TestCase): def setUp(self) -> None: - self.username = 'testuser' - self.email = 'testuser@email.com' - self.password = 'password1234567QWERTY' + self.username = "testuser" + self.email = "testuser@email.com" + self.password = "password1234567QWERTY" def test_signup_page_url(self): - response = self.client.get(reverse('signup')) + response = self.client.get(reverse("signup")) self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, template_name='registration/signup.html') + self.assertTemplateUsed(response, template_name="registration/signup.html") def test_signup_page_view_name(self): - response = self.client.get(reverse('signup')) + response = self.client.get(reverse("signup")) self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, template_name='registration/signup.html') + self.assertTemplateUsed(response, template_name="registration/signup.html") def test_signup_form(self): - response = self.client.post(reverse('signup'), data={ - 'username': self.username, - 'email': self.email, - 'password1': self.password, - 'password2': self.password - }) + response = self.client.post( + reverse("signup"), + data={ + "username": self.username, + "email": self.email, + "password1": self.password, + "password2": self.password, + }, + ) self.assertEqual(response.status_code, 200) users = get_user_model().objects.all() diff --git a/later42/tokens.py b/later42/tokens.py index a2fa72e..a6b1c41 100644 --- a/later42/tokens.py +++ b/later42/tokens.py @@ -5,8 +5,9 @@ import six class TokenGenerator(PasswordResetTokenGenerator): def _make_hash_value(self, user, timestamp): return ( - six.text_type(user.pk) + six.text_type(timestamp) + - six.text_type(user.is_active) + six.text_type(user.pk) + + six.text_type(timestamp) + + six.text_type(user.is_active) ) diff --git a/later42/urls.py b/later42/urls.py index 2fd712b..da21c3f 100644 --- a/later42/urls.py +++ b/later42/urls.py @@ -17,18 +17,29 @@ from django.contrib import admin from django.urls import path, include from django.contrib.auth.models import User + # from django.contrib.auth import views from rest_framework import routers, serializers, viewsets # from later42.forms import CustomLoginForm -from later42.views import account_activation, index, profile, api, api_token, reader, search, signup, about +from later42.views import ( + account_activation, + index, + profile, + api, + api_token, + reader, + search, + signup, + about, +) class UserSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = User - fields = ['url', 'username', 'email', 'is_staff'] + fields = ["url", "username", "email", "is_staff"] class UserViewSet(viewsets.ModelViewSet): @@ -37,22 +48,25 @@ class UserViewSet(viewsets.ModelViewSet): router = routers.DefaultRouter() -router.register(r'users', UserViewSet) +router.register(r"users", UserViewSet) urlpatterns = [ - path('admin/', admin.site.urls), - path('signup/', signup.register, name='signup'), - path(r'^activate/(?P