Mass refactoring
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Silver Ghost 2023-02-10 12:16:11 +03:00
parent 500f4dfbdd
commit 520acb9477
No known key found for this signature in database
52 changed files with 402 additions and 365 deletions

View File

@ -39,7 +39,7 @@ indent_style = tab
[docs/**.txt] [docs/**.txt]
max_line_length = 79 max_line_length = 79
[*.yml] [*.yaml]
indent_size = 2 indent_size = 2
[*.md] [*.md]

5
.flake8 Normal file
View File

@ -0,0 +1,5 @@
[flake8]
max-line-length = 120
per-file-ignores =
later42/views/index.py: E722
later42/views/reader.py: E722

2
.gitignore vendored
View File

@ -139,4 +139,4 @@ dmypy.json
cython_debug/ cython_debug/
db/*.sqlite3 db/*.sqlite3
static/* static/*

27
.pre-commit-config.yaml Normal file
View File

@ -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

View File

@ -29,7 +29,7 @@ SECRET_KEY | | The secret key to make hashes
DOMAIN | localhost | Domain name where the site is hosted DOMAIN | localhost | Domain name where the site is hosted
DB_TYPE | SQLite | Type of DB (SQLite, Postgres) DB_TYPE | SQLite | Type of DB (SQLite, Postgres)
DB_HOST | None | Host for Postgres DB 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_USER | None | User for Postgres DB
DB_PASS | None | Password for Postgres DB DB_PASS | None | Password for Postgres DB
REDIS_URL | redis://localhost:6379 | Connection string for Redis REDIS_URL | redis://localhost:6379 | Connection string for Redis

View File

@ -1,3 +1,3 @@
from .celery import app as celery_app from .celery import app as celery_app
__all__ = ('celery_app',) __all__ = ("celery_app",)

View File

@ -2,4 +2,4 @@ from django.contrib import admin
from .models.urls import URL from .models.urls import URL
admin.site.register(URL) admin.site.register(URL)

View File

@ -11,6 +11,6 @@ import os
from django.core.asgi import get_asgi_application 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() application = get_asgi_application()

View File

@ -3,15 +3,15 @@ import os
from celery import Celery from celery import Celery
# Set the default Django settings module for the 'celery' program. # 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 # Using a string here means the worker doesn't have to serialize
# the configuration object to child processes. # the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys # - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix. # 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. # Load task modules from all registered Django apps.
app.autodiscover_tasks() app.autodiscover_tasks()
@ -19,4 +19,4 @@ app.autodiscover_tasks()
@app.task(bind=True) @app.task(bind=True)
def debug_task(self): def debug_task(self):
print(f'Request: {self.request!r}') print(f"Request: {self.request!r}")

View File

@ -7,29 +7,24 @@ from django.contrib.auth.forms import UserCreationForm
class SignUpForm(UserCreationForm): class SignUpForm(UserCreationForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['username'].widget.attrs.update( self.fields["username"].widget.attrs.update({"class": "form-control"})
{'class': 'form-control'} self.fields["password1"].widget.attrs.update({"class": "form-control"})
) self.fields["password2"].widget.attrs.update({"class": "form-control"})
self.fields['password1'].widget.attrs.update( self.fields["username"].label = "Логин"
{'class': 'form-control'}
)
self.fields['password2'].widget.attrs.update(
{'class': 'form-control'}
)
self.fields['username'].label = 'Логин'
class Meta: class Meta:
model = User model = User
fields = ('username', 'email', 'password1', 'password2',) fields = (
"username",
"email",
"password1",
"password2",
)
class CustomLoginForm(AuthenticationForm): class CustomLoginForm(AuthenticationForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['username'].widget.attrs.update( self.fields["username"].widget.attrs.update({"class": "form-control"})
{'class': 'form-control'} self.fields["password"].widget.attrs.update({"class": "form-control"})
) self.fields["username"].label = "Логин"
self.fields['password'].widget.attrs.update(
{'class': 'form-control'}
)
self.fields['username'].label = 'Логин'

View File

@ -3,10 +3,10 @@ from newspaper import Article, Config
def sanitize_img_size(html: str): def sanitize_img_size(html: str):
soup = BeautifulSoup(html, 'html.parser') soup = BeautifulSoup(html, "html.parser")
for img in soup.find_all('img'): for img in soup.find_all("img"):
img['width'] = '100%' img["width"] = "100%"
img['height'] = 'auto' img["height"] = "auto"
return str(soup) return str(soup)

View File

@ -1,10 +1,8 @@
class ExceptionLoggingMiddleware(object): class ExceptionLoggingMiddleware(object):
def __init__(self, get_response): def __init__(self, get_response):
self.get_response = get_response self.get_response = get_response
def __call__(self, request): def __call__(self, request):
# Code to be executed for each request before # Code to be executed for each request before
# the view (and later middleware) are called. # the view (and later middleware) are called.
print(request.body) print(request.body)
@ -15,4 +13,4 @@ class ExceptionLoggingMiddleware(object):
response = self.get_response(request) response = self.get_response(request)
return response return response

View File

@ -6,7 +6,6 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
@ -15,12 +14,23 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='URL', name="URL",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)), (
('url', models.CharField(max_length=2000)), "id",
('title', models.CharField(max_length=2000)), models.AutoField(
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 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,
),
),
], ],
), ),
] ]

View File

@ -4,15 +4,14 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('later42', '0001_initial'), ("later42", "0001_initial"),
] ]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='url', model_name="url",
name='archived', name="archived",
field=models.BooleanField(default=False), field=models.BooleanField(default=False),
), ),
] ]

View File

@ -4,15 +4,14 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('later42', '0002_url_archived'), ("later42", "0002_url_archived"),
] ]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='url', model_name="url",
name='content', name="content",
field=models.TextField(blank=True, null=True), field=models.TextField(blank=True, null=True),
), ),
] ]

View File

@ -5,18 +5,27 @@ import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('later42', '0003_url_content'), ("later42", "0003_url_content"),
] ]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Articles', name="Articles",
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)), (
('content', models.TextField(blank=True, null=True)), "id",
('url', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='later42.url')), 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"
),
),
], ],
), ),
] ]

View File

@ -4,14 +4,13 @@ from django.db import migrations
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('later42', '0004_articles'), ("later42", "0004_articles"),
] ]
operations = [ operations = [
migrations.RenameModel( migrations.RenameModel(
old_name='Articles', old_name="Articles",
new_name='Article', new_name="Article",
), ),
] ]

View File

@ -4,28 +4,27 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('later42', '0005_rename_articles_article'), ("later42", "0005_rename_articles_article"),
] ]
operations = [ operations = [
migrations.RemoveField( migrations.RemoveField(
model_name='url', model_name="url",
name='content', name="content",
), ),
migrations.RemoveField( migrations.RemoveField(
model_name='url', model_name="url",
name='title', name="title",
), ),
migrations.AddField( migrations.AddField(
model_name='article', model_name="article",
name='short', name="short",
field=models.TextField(blank=True, null=True), field=models.TextField(blank=True, null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='article', model_name="article",
name='title', name="title",
field=models.CharField(blank=True, max_length=2000, null=True), field=models.CharField(blank=True, max_length=2000, null=True),
), ),
] ]

View File

@ -4,15 +4,14 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ 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 = [ operations = [
migrations.AddField( migrations.AddField(
model_name='article', model_name="article",
name='img', name="img",
field=models.URLField(blank=True, null=True), field=models.URLField(blank=True, null=True),
), ),
] ]

View File

@ -1,7 +1,7 @@
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import models from django.db import models
User._meta.get_field('email')._unique = True User._meta.get_field("email")._unique = True
class URL(models.Model): class URL(models.Model):

View File

@ -5,10 +5,10 @@ from rest_framework import serializers
class UserSerializer(serializers.HyperlinkedModelSerializer): class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta: class Meta:
model = User model = User
fields = ['url', 'username', 'email', 'groups'] fields = ["url", "username", "email", "groups"]
class GroupSerializer(serializers.HyperlinkedModelSerializer): class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta: class Meta:
model = Group model = Group
fields = ['url', 'name'] fields = ["url", "name"]

View File

@ -21,80 +21,81 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.getenv( 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! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.getenv('DEBUG', False) DEBUG = os.getenv("DEBUG", False)
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ["*"]
CSRF_TRUSTED_ORIGINS = ['https://' + os.getenv('DOMAIN', 'localhost')] CSRF_TRUSTED_ORIGINS = ["https://" + os.getenv("DOMAIN", "localhost")]
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
'later42', "later42",
'django.contrib.admin', "django.contrib.admin",
'django.contrib.auth', "django.contrib.auth",
'django.contrib.contenttypes', "django.contrib.contenttypes",
'django.contrib.sessions', "django.contrib.sessions",
'django.contrib.messages', "django.contrib.messages",
'django.contrib.staticfiles', "django.contrib.staticfiles",
'rest_framework', "rest_framework",
'rest_framework.authtoken', "rest_framework.authtoken",
] ]
MIDDLEWARE = [ MIDDLEWARE = [
# 'later42.logging.debug.ExceptionLoggingMiddleware', # 'later42.logging.debug.ExceptionLoggingMiddleware',
'django.middleware.security.SecurityMiddleware', "django.middleware.security.SecurityMiddleware",
'whitenoise.middleware.WhiteNoiseMiddleware', "whitenoise.middleware.WhiteNoiseMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware', "django.contrib.sessions.middleware.SessionMiddleware",
'django.middleware.common.CommonMiddleware', "django.middleware.common.CommonMiddleware",
'django.middleware.csrf.CsrfViewMiddleware', "django.middleware.csrf.CsrfViewMiddleware",
'django.contrib.auth.middleware.AuthenticationMiddleware', "django.contrib.auth.middleware.AuthenticationMiddleware",
'django.contrib.messages.middleware.MessageMiddleware', "django.contrib.messages.middleware.MessageMiddleware",
'django.middleware.clickjacking.XFrameOptionsMiddleware', "django.middleware.clickjacking.XFrameOptionsMiddleware",
] ]
ROOT_URLCONF = 'later42.urls' ROOT_URLCONF = "later42.urls"
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', "BACKEND": "django.template.backends.django.DjangoTemplates",
'DIRS': [], "DIRS": [],
'APP_DIRS': True, "APP_DIRS": True,
'OPTIONS': { "OPTIONS": {
'context_processors': [ "context_processors": [
'django.template.context_processors.debug', "django.template.context_processors.debug",
'django.template.context_processors.request', "django.template.context_processors.request",
'django.contrib.auth.context_processors.auth', "django.contrib.auth.context_processors.auth",
'django.contrib.messages.context_processors.messages', "django.contrib.messages.context_processors.messages",
], ],
}, },
}, },
] ]
WSGI_APPLICATION = 'later42.wsgi.application' WSGI_APPLICATION = "later42.wsgi.application"
# Database # Database
# https://docs.djangoproject.com/en/4.1/ref/settings/#databases # https://docs.djangoproject.com/en/4.1/ref/settings/#databases
DATABASES = { DATABASES = {
'default': { "default": {
'ENGINE': 'django.db.backends.sqlite3', "ENGINE": "django.db.backends.sqlite3",
'NAME': BASE_DIR / 'db/db.sqlite3', "NAME": BASE_DIR / "db/db.sqlite3",
} }
} }
if os.getenv('DB_TYPE', 'SQLite') == 'postgres': if os.getenv("DB_TYPE", "SQLite") == "postgres":
DATABASES = { DATABASES = {
'default': { "default": {
'ENGINE': 'django.db.backends.postgresql', "ENGINE": "django.db.backends.postgresql",
'HOST': os.environ.get('DB_HOST', None), "HOST": os.environ.get("DB_HOST", None),
'NAME': os.environ.get('DB_NAME', None), "NAME": os.environ.get("DB_NAME", None),
'USER': os.environ.get('DB_USER', None), "USER": os.environ.get("DB_USER", None),
'PASSWORD': os.environ.get('DB_PASS', None), "PASSWORD": os.environ.get("DB_PASS", None),
} }
} }
@ -104,16 +105,16 @@ if os.getenv('DB_TYPE', 'SQLite') == 'postgres':
AUTH_PASSWORD_VALIDATORS = [ 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 # Internationalization
# https://docs.djangoproject.com/en/4.1/topics/i18n/ # 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 USE_I18N = True
@ -133,7 +134,7 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.1/howto/static-files/ # https://docs.djangoproject.com/en/4.1/howto/static-files/
STATIC_URL = 'static/' STATIC_URL = "static/"
STATICFILES_DIRS = [ STATICFILES_DIRS = [
BASE_DIR / "later42/static", BASE_DIR / "later42/static",
] ]
@ -143,70 +144,68 @@ STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
# Default primary key field type # Default primary key field type
# https://docs.djangoproject.com/en/4.1/ref/settings/#default-auto-field # 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" LOGIN_REDIRECT_URL = "index"
LOGOUT_REDIRECT_URL = "index" LOGOUT_REDIRECT_URL = "index"
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
'PAGE_SIZE': 10, "PAGE_SIZE": 10,
'DEFAULT_AUTHENTICATION_CLASSES': ( "DEFAULT_AUTHENTICATION_CLASSES": (
'rest_framework.authentication.TokenAuthentication', "rest_framework.authentication.TokenAuthentication",
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
), ),
"DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
} }
# Celery Configuration Options # Celery Configuration Options
CELERY_TIMEZONE = TIME_ZONE CELERY_TIMEZONE = TIME_ZONE
CELERY_TASK_TRACK_STARTED = True CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_TIME_LIMIT = 30 * 60 CELERY_TASK_TIME_LIMIT = 30 * 60
CELERY_BROKER_URL = 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') CELERY_RESULT_BACKEND = os.getenv("REDIS_URL", "redis://localhost:6379")
URLS_PER_PAGE = 20 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: if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS', False) EMAIL_USE_TLS = os.getenv("EMAIL_USE_TLS", False)
EMAIL_HOST = os.getenv('EMAIL_HOST', 'smtp') EMAIL_HOST = os.getenv("EMAIL_HOST", "smtp")
EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', None) EMAIL_HOST_USER = os.getenv("EMAIL_HOST_USER", None)
EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', None) EMAIL_HOST_PASSWORD = os.getenv("EMAIL_HOST_PASSWORD", None)
EMAIL_PORT = os.getenv('EMAIL_PORT', '25') EMAIL_PORT = os.getenv("EMAIL_PORT", "25")
EMAIL_FROM = os.getenv('EMAIL_FROM', 'noreply@later42.com') EMAIL_FROM = os.getenv("EMAIL_FROM", "noreply@later42.com")
AIRBRAKE = dict( AIRBRAKE = dict(
project_id=os.getenv('AIRBRAKE_PROJECT_ID', None), project_id=os.getenv("AIRBRAKE_PROJECT_ID", None),
project_key=os.getenv('AIRBRAKE_PROJECT_KEY', None), project_key=os.getenv("AIRBRAKE_PROJECT_KEY", None),
environment=os.getenv('AIRBRAKE_ENVIRONMENT', 'development'), environment=os.getenv("AIRBRAKE_ENVIRONMENT", "development"),
) )
if AIRBRAKE['project_id'] is not None and AIRBRAKE['project_key'] is not None: if AIRBRAKE["project_id"] is not None and AIRBRAKE["project_key"] is not None:
MIDDLEWARE += ['pybrake.middleware.django.AirbrakeMiddleware'] MIDDLEWARE += ["pybrake.middleware.django.AirbrakeMiddleware"]
LOGGING = { LOGGING = {
'version': 1, "version": 1,
'disable_existing_loggers': False, "disable_existing_loggers": False,
'handlers': { "handlers": {
'airbrake': { "airbrake": {
'level': 'ERROR', "level": "ERROR",
'class': 'pybrake.LoggingHandler', "class": "pybrake.LoggingHandler",
}, },
}, },
'loggers': { "loggers": {
'app': { "app": {
'handlers': ['airbrake'], "handlers": ["airbrake"],
'level': 'ERROR', "level": "ERROR",
'propagate': True, "propagate": True,
}, },
}, },
} }
SENTRY_DSN = os.getenv('SENTRY_DSN', None) SENTRY_DSN = os.getenv("SENTRY_DSN", None)
if SENTRY_DSN is not None: if SENTRY_DSN is not None:
import sentry_sdk import sentry_sdk

View File

@ -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"} {"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"}

View File

@ -11245,4 +11245,4 @@ header.masthead .site-heading .h1 {
.post-preview > a > .post-title { .post-preview > a > .post-title {
font-size: 2.25rem; font-size: 2.25rem;
} }
} }

View File

@ -13,7 +13,11 @@ AIRBRAKE_PROJECT_ID = os.getenv("AIRBRAKE_PROJECT_ID", None)
AIRBRAKE_PROJECT_KEY = os.getenv("AIRBRAKE_PROJECT_KEY", None) AIRBRAKE_PROJECT_KEY = os.getenv("AIRBRAKE_PROJECT_KEY", None)
if AIRBRAKE_PROJECT_ID is not None and AIRBRAKE_PROJECT_KEY is not 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) patch_celery(notifier)

View File

@ -32,4 +32,4 @@
<span class="fa-solid fa-arrow-up-right-from-square"></span>&nbsp;Открыть <a href="{{ url.url }}">оригинал ссылки</a>. <span class="fa-solid fa-arrow-up-right-from-square"></span>&nbsp;Открыть <a href="{{ url.url }}">оригинал ссылки</a>.
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -3,4 +3,4 @@
Ссылка для активации аккаунта: Ссылка для активации аккаунта:
https://{{ domain }}{% url 'activate' uidb64=uid token=token %} https://{{ domain }}{% url 'activate' uidb64=uid token=token %}
{% endautoescape %} {% endautoescape %}

View File

@ -3,4 +3,4 @@
{% block content %} {% block content %}
<p>Пароль успешно изменен.</p> <p>Пароль успешно изменен.</p>
<p>Перейти в <a href="{% url 'profile' %}">Профиль</a>.</p> <p>Перейти в <a href="{% url 'profile' %}">Профиль</a>.</p>
{% endblock %} {% endblock %}

View File

@ -3,4 +3,4 @@
{% block content %} {% block content %}
<p>Пароль успешно изменен.</p> <p>Пароль успешно изменен.</p>
<p>Вы можете <a href="{% url 'login' %}">войти</a>.</p> <p>Вы можете <a href="{% url 'login' %}">войти</a>.</p>
{% endblock %} {% endblock %}

View File

@ -29,4 +29,4 @@
<p>Ссылка неверная или уже была использована.<br /> <p>Ссылка неверная или уже была использована.<br />
Попробуйте сбросить пароль еще раз.</p> Попробуйте сбросить пароль еще раз.</p>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -3,4 +3,4 @@
{% block content %} {% block content %}
<p>Сообщение успешно отправлено.</p> <p>Сообщение успешно отправлено.</p>
<p>Вы должны получить его в скором времени.</p> <p>Вы должны получить его в скором времени.</p>
{% endblock %} {% endblock %}

View File

@ -1,25 +1,20 @@
from django.contrib.auth.models import User 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 django.urls import reverse
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from rest_framework.test import APIClient from rest_framework.test import APIClient
from later42.models.urls import URL
class ApiTests(TestCase): class ApiTests(TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.username = 'testuser1' self.username = "testuser1"
self.email = 'testuser1@email.com' self.email = "testuser1@email.com"
self.password = 'password1234567QWERTY' self.password = "password1234567QWERTY"
self.user = User.objects.create( self.user = User.objects.create(username=self.username, email=self.email)
username=self.username,
email=self.email
)
self.user.set_password(self.password) self.user.set_password(self.password)
self.user.save() self.user.save()
self.url = 'https://google.com' self.url = "https://google.com"
self.c = Client() self.c = Client()
self.c.login(username=self.username, password=self.password) self.c.login(username=self.username, password=self.password)
@ -29,7 +24,7 @@ class ApiTests(TestCase):
def test_url_create(self): def test_url_create(self):
token = Token.objects.get(user=self.user) token = Token.objects.get(user=self.user)
client = APIClient() client = APIClient()
client.credentials(HTTP_AUTHORIZATION='Token ' + token.key) client.credentials(HTTP_AUTHORIZATION="Token " + token.key)
response = client.post(reverse('urls') + f'?url={self.url}') response = client.post(reverse("urls") + f"?url={self.url}")
assert response.status_code == 200 assert response.status_code == 200
self.assertContains(response, 'success') self.assertContains(response, "success")

View File

@ -6,24 +6,21 @@ from rest_framework.authtoken.models import Token
class ApiKeyTests(TestCase): class ApiKeyTests(TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.username = 'testuser1' self.username = "testuser1"
self.email = 'testuser1@email.com' self.email = "testuser1@email.com"
self.password = 'password1234567QWERTY' self.password = "password1234567QWERTY"
self.user = User.objects.create( self.user = User.objects.create(username=self.username, email=self.email)
username=self.username,
email=self.email
)
self.user.set_password(self.password) self.user.set_password(self.password)
self.user.save() self.user.save()
self.c = Client() self.c = Client()
self.c.login(username=self.username, password=self.password) self.c.login(username=self.username, password=self.password)
self.assertTrue(self.user.is_authenticated) 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): def test_api_key_creation(self):
assert self.response.status_code == 302 assert self.response.status_code == 302
assert self.response.url == '/profile/' assert self.response.url == "/profile/"
def test_api_key_created(self): def test_api_key_created(self):
token = Token.objects.get(user=self.user) token = Token.objects.get(user=self.user)
@ -31,6 +28,6 @@ class ApiKeyTests(TestCase):
def test_api_key_reset(self): def test_api_key_reset(self):
token_old = Token.objects.get(user=self.user) 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) token_new = Token.objects.get(user=self.user)
assert token_old != token_new assert token_old != token_new

View File

@ -1,20 +1,16 @@
from django.contrib.auth.models import User 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 django.urls import reverse
from rest_framework.authtoken.models import Token
from later42.models.urls import URL from later42.models.urls import URL
class IndexTests(TestCase): class IndexTests(TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.username = 'testuser1' self.username = "testuser1"
self.email = 'testuser1@email.com' self.email = "testuser1@email.com"
self.password = 'password1234567QWERTY' self.password = "password1234567QWERTY"
self.user = User.objects.create( self.user = User.objects.create(username=self.username, email=self.email)
username=self.username,
email=self.email
)
self.user.set_password(self.password) self.user.set_password(self.password)
self.user.save() self.user.save()
@ -23,11 +19,8 @@ class IndexTests(TestCase):
self.assertTrue(self.user.is_authenticated) self.assertTrue(self.user.is_authenticated)
def test_url_delete(self): def test_url_delete(self):
url = URL.objects.create( url = URL.objects.create(url="https://www.google.com/", user=self.user)
url='https://www.google.com/',
user=self.user
)
url.save() 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) url = URL.objects.filter(id=url.id)
assert len(url) == 0 assert len(url) == 0

View File

@ -5,13 +5,10 @@ from django.urls import reverse
class LoginPageTests(TestCase): class LoginPageTests(TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.username = 'testuser1' self.username = "testuser1"
self.email = 'testuser1@email.com' self.email = "testuser1@email.com"
self.password = 'password1234567QWERTY' self.password = "password1234567QWERTY"
self.user = User.objects.create( self.user = User.objects.create(username=self.username, email=self.email)
username=self.username,
email=self.email
)
self.user.set_password(self.password) self.user.set_password(self.password)
self.user.save() self.user.save()
@ -20,22 +17,22 @@ class LoginPageTests(TestCase):
self.assertTrue(self.user.is_authenticated) self.assertTrue(self.user.is_authenticated)
def test_login_page(self): def test_login_page(self):
response = self.client.get(reverse('login')) response = self.client.get(reverse("login"))
assert response.status_code == 200 assert response.status_code == 200
def test_index_page_after_login(self): 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.status_code == 200
assert response.context['user'].is_authenticated assert response.context["user"].is_authenticated
self.assertTemplateUsed(response, 'index.html') self.assertTemplateUsed(response, "index.html")
self.assertContains(response, '/accounts/logout/') self.assertContains(response, "/accounts/logout/")
def test_profile_page_template(self): def test_profile_page_template(self):
response = self.c.get(reverse('profile')) response = self.c.get(reverse("profile"))
assert response.status_code == 200 assert response.status_code == 200
self.assertTemplateUsed(response, 'profile.html') self.assertTemplateUsed(response, "profile.html")
def test_api_key_creation_button(self): def test_api_key_creation_button(self):
response = self.c.get(reverse('profile')) response = self.c.get(reverse("profile"))
assert response.status_code == 200 assert response.status_code == 200
self.assertContains(response, 'id="createbutton"') self.assertContains(response, 'id="createbutton"')

View File

@ -5,15 +5,15 @@ from django.urls import reverse
class PageTests(TestCase): class PageTests(TestCase):
def test_index_page_url(self): 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.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): 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.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): 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) self.assertEqual(response.status_code, 302)

View File

@ -6,27 +6,30 @@ from django.urls import reverse
class SignUpPageTests(TestCase): class SignUpPageTests(TestCase):
def setUp(self) -> None: def setUp(self) -> None:
self.username = 'testuser' self.username = "testuser"
self.email = 'testuser@email.com' self.email = "testuser@email.com"
self.password = 'password1234567QWERTY' self.password = "password1234567QWERTY"
def test_signup_page_url(self): 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.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): 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.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): def test_signup_form(self):
response = self.client.post(reverse('signup'), data={ response = self.client.post(
'username': self.username, reverse("signup"),
'email': self.email, data={
'password1': self.password, "username": self.username,
'password2': self.password "email": self.email,
}) "password1": self.password,
"password2": self.password,
},
)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
users = get_user_model().objects.all() users = get_user_model().objects.all()

View File

@ -5,8 +5,9 @@ import six
class TokenGenerator(PasswordResetTokenGenerator): class TokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp): def _make_hash_value(self, user, timestamp):
return ( return (
six.text_type(user.pk) + six.text_type(timestamp) + six.text_type(user.pk)
six.text_type(user.is_active) + six.text_type(timestamp)
+ six.text_type(user.is_active)
) )

View File

@ -17,18 +17,29 @@ from django.contrib import admin
from django.urls import path, include from django.urls import path, include
from django.contrib.auth.models import User from django.contrib.auth.models import User
# from django.contrib.auth import views # from django.contrib.auth import views
from rest_framework import routers, serializers, viewsets from rest_framework import routers, serializers, viewsets
# from later42.forms import CustomLoginForm # 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 UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta: class Meta:
model = User model = User
fields = ['url', 'username', 'email', 'is_staff'] fields = ["url", "username", "email", "is_staff"]
class UserViewSet(viewsets.ModelViewSet): class UserViewSet(viewsets.ModelViewSet):
@ -37,22 +48,25 @@ class UserViewSet(viewsets.ModelViewSet):
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register(r'users', UserViewSet) router.register(r"users", UserViewSet)
urlpatterns = [ urlpatterns = [
path('admin/', admin.site.urls), path("admin/", admin.site.urls),
path('signup/', signup.register, name='signup'), path("signup/", signup.register, name="signup"),
path(r'^activate/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', path(
account_activation.activate, name='activate'), r"^activate/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$",
path('accounts/', include('django.contrib.auth.urls')), account_activation.activate,
path('profile/', profile.get, name='profile'), name="activate",
path('api/url/', api.URL.as_view(), name='urls'), ),
path('delete/<int:url_id>', index.delete, name='delete'), path("accounts/", include("django.contrib.auth.urls")),
path('api_token/', api_token.create, name='api_token'), path("profile/", profile.get, name="profile"),
path('', index.get, name='index'), path("api/url/", api.URL.as_view(), name="urls"),
path('about/', about.get, name='about'), path("delete/<int:url_id>", index.delete, name="delete"),
path('archive/', index.archive, name='archive'), path("api_token/", api_token.create, name="api_token"),
path('archive/<int:url_id>', index.archive, name='archive_url'), path("", index.get, name="index"),
path('reader/<int:url_id>', reader.get, name='reader'), path("about/", about.get, name="about"),
path('search/', search.search, name='search'), path("archive/", index.archive, name="archive"),
path("archive/<int:url_id>", index.archive, name="archive_url"),
path("reader/<int:url_id>", reader.get, name="reader"),
path("search/", search.search, name="search"),
] ]

View File

@ -2,4 +2,4 @@ from django.shortcuts import render
def get(request): def get(request):
return render(request, 'about.html') return render(request, "about.html")

View File

@ -1,16 +1,13 @@
import django import django
from django.core.mail import EmailMessage from django.contrib.auth import login
from django.contrib.auth.models import User from django.contrib.auth.models import User
from later42.tokens import account_activation_token
from django.template.loader import render_to_string
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.http import HttpResponse from django.http import HttpResponse
from django.shortcuts import render, redirect from django.shortcuts import redirect
from django.contrib.auth import login, authenticate
from later42.forms import SignUpForm
from django.contrib.sites.shortcuts import get_current_site
from django.utils.encoding import force_bytes
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.http import urlsafe_base64_decode
from later42.tokens import account_activation_token
django.utils.encoding.force_text = force_str django.utils.encoding.force_text = force_str
@ -24,6 +21,6 @@ def activate(request, uidb64, token):
user.is_active = True user.is_active = True
user.save() user.save()
login(request, user) login(request, user)
return redirect('index') return redirect("index")
else: else:
return HttpResponse('Activation link is invalid!') return HttpResponse("Activation link is invalid!")

View File

@ -1,36 +1,36 @@
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.validators import URLValidator
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from django.core.validators import URLValidator
from later42.models.urls import URL as URLModel from later42.models.urls import URL as URLModel
from later42.tasks import get_url_content_task from later42.tasks import get_url_content_task
from django.conf import settings
def validate_url(to_validate:str) -> bool: def validate_url(to_validate: str) -> bool:
validator = URLValidator() validator = URLValidator()
try: try:
validator(to_validate) validator(to_validate)
return True return True
except ValidationError as exception: except ValidationError:
return False return False
class URL(APIView): class URL(APIView):
def post(self, request, format=None): def post(self, request, format=None):
if validate_url(request.GET.get('url')): if validate_url(request.GET.get("url")):
get_url_content_task.delay(request.GET.get('url'), request.user.id) get_url_content_task.delay(request.GET.get("url"), request.user.id)
return Response({'status': 'success'}) return Response({"status": "success"})
else: else:
return Response({'status': 'error'}) return Response({"status": "error"})
def delete(self, request, format=None): def delete(self, request, format=None):
id = request.GET.get('id') id = request.GET.get("id")
if id: if id:
url = URLModel.objects.filter(id=id).first() url = URLModel.objects.filter(id=id).first()
if url: if url:
url.archived = True url.archived = True
url.save() url.save()
return Response({'status': 'success'}) return Response({"status": "success"})
else: else:
return Response({'status': 'error'}) return Response({"status": "error"})

View File

@ -10,4 +10,4 @@ def create(request):
token.delete() token.delete()
token = Token.objects.create(user=request.user) token = Token.objects.create(user=request.user)
token.save() token.save()
return redirect('profile') return redirect("profile")

View File

@ -1,8 +1,7 @@
from multiprocessing import context
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.core.paginator import Paginator
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.paginator import Paginator
from django.shortcuts import redirect, render
from later42.models.article import Article from later42.models.article import Article
from later42.models.urls import URL from later42.models.urls import URL
@ -11,36 +10,36 @@ from later42.models.urls import URL
def get(request): def get(request):
data = {} data = {}
try: try:
urls = URL.objects.filter( urls = URL.objects.filter(user=request.user, archived=False).order_by("-id")
user=request.user, archived=False).order_by('-id') data = (
data = Article.objects.filter( Article.objects.filter(url__in=urls).select_related("url").order_by("-id")
url__in=urls).select_related('url').order_by('-id') )
except: except:
urls = [] urls = []
context = {'data': data} context = {"data": data}
return render(request, 'index.html', context) return render(request, "index.html", context)
@login_required @login_required
def archive(request, url_id=None): def archive(request, url_id=None):
if url_id: if url_id:
URL.objects.filter(id=url_id, user=request.user).update(archived=True) URL.objects.filter(id=url_id, user=request.user).update(archived=True)
return redirect('index') return redirect("index")
try: try:
urls = URL.objects.filter( urls = URL.objects.filter(user=request.user, archived=True).order_by("-id")
user=request.user, archived=True).order_by('-id') data = (
data = Article.objects.filter( Article.objects.filter(url__in=urls).select_related("url").order_by("-id")
url__in=urls).select_related('url').order_by('-id') )
paginator = Paginator(data, settings.URLS_PER_PAGE) paginator = Paginator(data, settings.URLS_PER_PAGE)
page_number = request.GET.get('page') page_number = request.GET.get("page")
data = paginator.get_page(page_number) data = paginator.get_page(page_number)
except: except:
data = [] data = []
context = {'data': data} context = {"data": data}
return render(request, 'archive.html', context) return render(request, "archive.html", context)
@login_required @login_required
def delete(request, url_id): def delete(request, url_id):
URL.objects.filter(id=url_id, user=request.user).delete() URL.objects.filter(id=url_id, user=request.user).delete()
return redirect('archive') return redirect("archive")

View File

@ -10,9 +10,8 @@ def get(request):
user = User.objects.get(username=request.user) user = User.objects.get(username=request.user)
if token: if token:
context = {'token': token[0], 'user': user} context = {"token": token[0], "user": user}
else: else:
context = {'token': None, 'user': user} context = {"token": None, "user": user}
return render(request, 'profile.html', context)
return render(request, "profile.html", context)

View File

@ -7,16 +7,15 @@ from later42.models.urls import URL
@login_required @login_required
def get(request, url_id=None): def get(request, url_id=None):
url = URL.objects.get( url = URL.objects.get(user=request.user, id=url_id)
user=request.user, id=url_id)
content = {} content = {}
try: try:
article = Article.objects.get(url=url) article = Article.objects.get(url=url)
content['title'] = article.title content["title"] = article.title
content['url'] = url.url content["url"] = url.url
content['rich_content'] = sanitize_img_size(article.content) content["rich_content"] = sanitize_img_size(article.content)
except: except:
content = get_content(url.url) content = get_content(url.url)
content['rich_content'] = sanitize_img_size(content.article_html) content["rich_content"] = sanitize_img_size(content.article_html)
context = {'url': url, 'content': content} context = {"url": url, "content": content}
return render(request, 'reader.html', context) return render(request, "reader.html", context)

View File

@ -5,25 +5,24 @@ from django.db.models import Q
from django.shortcuts import render from django.shortcuts import render
from later42.models.article import Article from later42.models.article import Article
from later42.models.urls import URL as URL
@login_required @login_required
def search(request): def search(request):
pattern = request.POST.get('search') pattern = request.POST.get("search")
context = {} context = {}
if request.method == 'GET': if request.method == "GET":
return render(request, 'search.html', context) return render(request, "search.html", context)
elif request.method == 'POST': elif request.method == "POST":
urls = URL.objects.filter(
user=request.user).order_by('-id')
data = Article.objects.filter( data = Article.objects.filter(
Q(title__contains=pattern) | Q(title__contains=pattern)
Q(content__contains=pattern) | | Q(content__contains=pattern)
Q(short__contains=pattern) | | Q(short__contains=pattern)
Q(url__url__contains=pattern), url__user_id=request.user.id).select_related('url') | Q(url__url__contains=pattern),
url__user_id=request.user.id,
).select_related("url")
paginator = Paginator(data, settings.URLS_PER_PAGE) paginator = Paginator(data, settings.URLS_PER_PAGE)
page_number = request.GET.get('page') page_number = request.GET.get("page")
data = paginator.get_page(page_number) data = paginator.get_page(page_number)
context = {'data': data} context = {"data": data}
return render(request, 'search.html', context) return render(request, "search.html", context)

View File

@ -1,41 +1,43 @@
from django.contrib.auth import authenticate, login
from django.shortcuts import redirect, render
from django.contrib.sites.shortcuts import get_current_site
from django.template.loader import render_to_string
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.utils.encoding import force_bytes, force_text
from django.core.mail import EmailMessage
from django.conf import settings from django.conf import settings
from django.contrib.sites.shortcuts import get_current_site
from django.core.mail import EmailMessage
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from later42.forms import SignUpForm from later42.forms import SignUpForm
from later42.tokens import account_activation_token from later42.tokens import account_activation_token
def register(request): def register(request):
if request.method == 'POST': if request.method == "POST":
form = SignUpForm(request.POST) form = SignUpForm(request.POST)
if form.is_valid(): if form.is_valid():
user = form.save(commit=False) user = form.save(commit=False)
user.is_active = False user.is_active = False
user.save() user.save()
raw_password = form.cleaned_data.get('password1') # raw_password = form.cleaned_data.get("password1")
current_site = get_current_site(request) current_site = get_current_site(request)
mail_subject = 'Later42: Активация аккаунта' mail_subject = "Later42: Активация аккаунта"
message = render_to_string('registration/email_activation.html', { message = render_to_string(
'user': user, "registration/email_activation.html",
'domain': current_site.domain, {
'uid': urlsafe_base64_encode(force_bytes(user.pk)), "user": user,
'token': account_activation_token.make_token(user), "domain": current_site.domain,
}) "uid": urlsafe_base64_encode(force_bytes(user.pk)),
to_email = form.cleaned_data.get('email') "token": account_activation_token.make_token(user),
},
)
to_email = form.cleaned_data.get("email")
email = EmailMessage( email = EmailMessage(
mail_subject, message, to=[ mail_subject, message, to=[to_email], from_email=settings.EMAIL_FROM
to_email], from_email=settings.EMAIL_FROM
) )
email.send() email.send()
return render(request, 'registration/email_sent.html') return render(request, "registration/email_sent.html")
else: else:
form = SignUpForm() form = SignUpForm()
return render(request, 'registration/signup.html', {'form': form}) return render(request, "registration/signup.html", {"form": form})

View File

@ -11,6 +11,6 @@ import os
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'later42.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "later42.settings")
application = get_wsgi_application() application = get_wsgi_application()

View File

@ -6,7 +6,7 @@ import sys
def main(): def main():
"""Run administrative tasks.""" """Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'later42.settings') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "later42.settings")
try: try:
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
except ImportError as exc: except ImportError as exc:
@ -18,5 +18,5 @@ def main():
execute_from_command_line(sys.argv) execute_from_command_line(sys.argv)
if __name__ == '__main__': if __name__ == "__main__":
main() main()

View File

@ -1,8 +1,8 @@
-r requirements.txt -r requirements.txt
black black
django-stubs
isort isort
mypy
pylint pylint
pylint-django pylint-django
mypy
django-stubs

View File

@ -1,13 +1,13 @@
bs4==0.0.1
celery[redis]==5.2.7
Django==4.1.3 Django==4.1.3
djangorestframework==3.14.0
gunicorn==20.1.0 gunicorn==20.1.0
loguru==0.6.0 loguru==0.6.0
bs4==0.0.1
requests==2.28.1
djangorestframework==3.14.0
whitenoise==6.2.0
psycopg2-binary==2.9.5
six==1.16.0
celery[redis]==5.2.7
pybrake==1.10.0
sentry-sdk==1.11.0
newspaper3k==0.2.8 newspaper3k==0.2.8
psycopg2-binary==2.9.5
pybrake==1.10.0
requests==2.28.1
sentry-sdk==1.11.0
six==1.16.0
whitenoise==6.2.0