Merge branch 'develop' into feature/gm-148
# Conflicts: # apps/news/admin.py
This commit is contained in:
commit
4796fce784
|
|
@ -81,7 +81,6 @@ class LoginByUsernameOrEmailSerializer(SourceSerializerMixin,
|
||||||
"""Serializer for login user"""
|
"""Serializer for login user"""
|
||||||
# REQUEST
|
# REQUEST
|
||||||
username_or_email = serializers.CharField(write_only=True)
|
username_or_email = serializers.CharField(write_only=True)
|
||||||
password = serializers.CharField(write_only=True)
|
|
||||||
|
|
||||||
# For cookie properties (Max-Age)
|
# For cookie properties (Max-Age)
|
||||||
remember = serializers.BooleanField(write_only=True)
|
remember = serializers.BooleanField(write_only=True)
|
||||||
|
|
@ -101,21 +100,24 @@ class LoginByUsernameOrEmailSerializer(SourceSerializerMixin,
|
||||||
'refresh_token',
|
'refresh_token',
|
||||||
'access_token',
|
'access_token',
|
||||||
)
|
)
|
||||||
|
extra_kwargs = {
|
||||||
|
'password': {'write_only': True}
|
||||||
|
}
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
"""Override validate method"""
|
"""Override validate method"""
|
||||||
username_or_email = attrs.pop('username_or_email')
|
username_or_email = attrs.pop('username_or_email')
|
||||||
password = attrs.pop('password')
|
password = attrs.pop('password')
|
||||||
user_qs = account_models.User.objects.filter(Q(username=username_or_email) |
|
user_qs = account_models.User.objects.filter(Q(username=username_or_email) |
|
||||||
(Q(email=username_or_email)))
|
Q(email=username_or_email))
|
||||||
if not user_qs.exists():
|
if not user_qs.exists():
|
||||||
raise utils_exceptions.UserNotFoundError()
|
raise utils_exceptions.WrongAuthCredentials()
|
||||||
else:
|
else:
|
||||||
user = user_qs.first()
|
user = user_qs.first()
|
||||||
authentication = authenticate(username=user.get_username(),
|
authentication = authenticate(username=user.get_username(),
|
||||||
password=password)
|
password=password)
|
||||||
if not authentication:
|
if not authentication:
|
||||||
raise utils_exceptions.UserNotFoundError()
|
raise utils_exceptions.WrongAuthCredentials()
|
||||||
self.instance = user
|
self.instance = user
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
@ -127,10 +129,6 @@ class LoginByUsernameOrEmailSerializer(SourceSerializerMixin,
|
||||||
return super().to_representation(instance)
|
return super().to_representation(instance)
|
||||||
|
|
||||||
|
|
||||||
class LogoutSerializer(SourceSerializerMixin):
|
|
||||||
"""Serializer for Logout endpoint."""
|
|
||||||
|
|
||||||
|
|
||||||
class RefreshTokenSerializer(SourceSerializerMixin):
|
class RefreshTokenSerializer(SourceSerializerMixin):
|
||||||
"""Serializer for refresh token view"""
|
"""Serializer for refresh token view"""
|
||||||
refresh_token = serializers.CharField(read_only=True)
|
refresh_token = serializers.CharField(read_only=True)
|
||||||
|
|
@ -169,7 +167,3 @@ class RefreshTokenSerializer(SourceSerializerMixin):
|
||||||
class OAuth2Serialzier(SourceSerializerMixin):
|
class OAuth2Serialzier(SourceSerializerMixin):
|
||||||
"""Serializer OAuth2 authorization"""
|
"""Serializer OAuth2 authorization"""
|
||||||
token = serializers.CharField(max_length=255)
|
token = serializers.CharField(max_length=255)
|
||||||
|
|
||||||
|
|
||||||
class OAuth2LogoutSerializer(SourceSerializerMixin):
|
|
||||||
"""Serializer for logout"""
|
|
||||||
|
|
|
||||||
|
|
@ -27,24 +27,6 @@ from utils.permissions import IsAuthenticatedAndTokenIsValid
|
||||||
from utils.views import JWTGenericViewMixin
|
from utils.views import JWTGenericViewMixin
|
||||||
|
|
||||||
|
|
||||||
# Mixins
|
|
||||||
# JWTAuthView mixin
|
|
||||||
class JWTAuthViewMixin(JWTGenericViewMixin):
|
|
||||||
"""Mixin for authentication views"""
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
"""Implement POST method"""
|
|
||||||
serializer = self.get_serializer(data=request.data)
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
response = Response(serializer.data, status=status.HTTP_200_OK)
|
|
||||||
access_token = serializer.data.get('access_token')
|
|
||||||
refresh_token = serializer.data.get('refresh_token')
|
|
||||||
return self._put_cookies_in_response(
|
|
||||||
cookies=self._put_data_in_cookies(access_token=access_token,
|
|
||||||
refresh_token=refresh_token),
|
|
||||||
response=response)
|
|
||||||
|
|
||||||
|
|
||||||
# OAuth2
|
# OAuth2
|
||||||
class BaseOAuth2ViewMixin(generics.GenericAPIView):
|
class BaseOAuth2ViewMixin(generics.GenericAPIView):
|
||||||
"""BaseMixin for classic auth views"""
|
"""BaseMixin for classic auth views"""
|
||||||
|
|
@ -112,6 +94,7 @@ class OAuth2SignUpView(OAuth2ViewMixin, JWTGenericViewMixin):
|
||||||
# Preparing request data
|
# Preparing request data
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
request_data = self.prepare_request_data(serializer.validated_data)
|
request_data = self.prepare_request_data(serializer.validated_data)
|
||||||
source = serializer.validated_data.get('source')
|
source = serializer.validated_data.get('source')
|
||||||
request_data.update({
|
request_data.update({
|
||||||
|
|
@ -196,7 +179,7 @@ class ConfirmationEmailView(JWTGenericViewMixin):
|
||||||
|
|
||||||
|
|
||||||
# Login by username|email + password
|
# Login by username|email + password
|
||||||
class LoginByUsernameOrEmailView(JWTAuthViewMixin):
|
class LoginByUsernameOrEmailView(JWTGenericViewMixin):
|
||||||
"""Login by email and password"""
|
"""Login by email and password"""
|
||||||
permission_classes = (permissions.AllowAny,)
|
permission_classes = (permissions.AllowAny,)
|
||||||
serializer_class = serializers.LoginByUsernameOrEmailSerializer
|
serializer_class = serializers.LoginByUsernameOrEmailSerializer
|
||||||
|
|
@ -205,10 +188,12 @@ class LoginByUsernameOrEmailView(JWTAuthViewMixin):
|
||||||
"""Implement POST method"""
|
"""Implement POST method"""
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
response = Response(serializer.data, status=status.HTTP_200_OK)
|
response = Response(serializer.data, status=status.HTTP_200_OK)
|
||||||
access_token = serializer.data.get('access_token')
|
access_token = serializer.data.get('access_token')
|
||||||
refresh_token = serializer.data.get('refresh_token')
|
refresh_token = serializer.data.get('refresh_token')
|
||||||
is_permanent = serializer.validated_data.get('remember')
|
is_permanent = serializer.validated_data.get('remember')
|
||||||
|
|
||||||
return self._put_cookies_in_response(
|
return self._put_cookies_in_response(
|
||||||
cookies=self._put_data_in_cookies(access_token=access_token,
|
cookies=self._put_data_in_cookies(access_token=access_token,
|
||||||
refresh_token=refresh_token,
|
refresh_token=refresh_token,
|
||||||
|
|
@ -243,9 +228,11 @@ class RefreshTokenView(JWTGenericViewMixin):
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=request.data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
response = Response(serializer.data, status=status.HTTP_201_CREATED)
|
response = Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
access_token = serializer.data.get('access_token')
|
access_token = serializer.data.get('access_token')
|
||||||
refresh_token = serializer.data.get('refresh_token')
|
refresh_token = serializer.data.get('refresh_token')
|
||||||
|
|
||||||
return self._put_cookies_in_response(
|
return self._put_cookies_in_response(
|
||||||
cookies=self._put_data_in_cookies(access_token=access_token,
|
cookies=self._put_data_in_cookies(access_token=access_token,
|
||||||
refresh_token=refresh_token),
|
refresh_token=refresh_token),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from news import models
|
from news import models
|
||||||
|
from .tasks import send_email_with_news
|
||||||
|
|
||||||
@admin.register(models.NewsType)
|
@admin.register(models.NewsType)
|
||||||
class NewsTypeAdmin(admin.ModelAdmin):
|
class NewsTypeAdmin(admin.ModelAdmin):
|
||||||
|
|
@ -10,9 +11,21 @@ class NewsTypeAdmin(admin.ModelAdmin):
|
||||||
list_display_links = ['id', 'name']
|
list_display_links = ['id', 'name']
|
||||||
|
|
||||||
|
|
||||||
|
def send_email_action(modeladmin, request, queryset):
|
||||||
|
news_ids = list(queryset.values_list("id", flat=True))
|
||||||
|
if settings.USE_CELERY:
|
||||||
|
send_email_with_news.delay(news_ids)
|
||||||
|
else:
|
||||||
|
send_email_with_news(news_ids)
|
||||||
|
|
||||||
|
|
||||||
|
send_email_action.short_description = "Send the selected news by email"
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.News)
|
@admin.register(models.News)
|
||||||
class NewsAdmin(admin.ModelAdmin):
|
class NewsAdmin(admin.ModelAdmin):
|
||||||
"""News admin."""
|
"""News admin."""
|
||||||
|
actions = [send_email_action]
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.NewsGallery)
|
@admin.register(models.NewsGallery)
|
||||||
|
|
|
||||||
28
apps/news/tasks.py
Normal file
28
apps/news/tasks.py
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
from celery import shared_task
|
||||||
|
from django.core.mail import send_mail
|
||||||
|
from notification.models import Subscriber
|
||||||
|
from news import models
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.conf import settings
|
||||||
|
from smtplib import SMTPException
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def send_email_with_news(news_ids):
|
||||||
|
|
||||||
|
subscribers = Subscriber.objects.filter(state=Subscriber.USABLE)
|
||||||
|
sent_news = models.News.objects.filter(id__in=news_ids)
|
||||||
|
|
||||||
|
for s in subscribers:
|
||||||
|
try:
|
||||||
|
for n in sent_news:
|
||||||
|
send_mail("G&M News", render_to_string(settings.NEWS_EMAIL_TEMPLATE,
|
||||||
|
{"title": n.title.get(s.locale),
|
||||||
|
"subtitle": n.subtitle.get(s.locale),
|
||||||
|
"description": n.description.get(s.locale),
|
||||||
|
"code": s.update_code,
|
||||||
|
"domain_uri": settings.DOMAIN_URI,
|
||||||
|
"country_code": s.country_code}),
|
||||||
|
settings.EMAIL_HOST_USER, [s.send_to], fail_silently=False)
|
||||||
|
except SMTPException:
|
||||||
|
continue
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from notification.views import common
|
from notification.views import common
|
||||||
|
|
||||||
|
app_name = "notification"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('subscribe/', common.SubscribeView.as_view(), name='subscribe'),
|
path('subscribe/', common.SubscribeView.as_view(), name='subscribe'),
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ class WrongAuthCredentials(AuthErrorMixin):
|
||||||
"""
|
"""
|
||||||
The exception should be raised when credentials is not valid for this user
|
The exception should be raised when credentials is not valid for this user
|
||||||
"""
|
"""
|
||||||
default_detail = _('Wrong authorization credentials')
|
default_detail = _('Incorrect login or password.')
|
||||||
|
|
||||||
|
|
||||||
class FavoritesError(exceptions.APIException):
|
class FavoritesError(exceptions.APIException):
|
||||||
|
|
|
||||||
|
|
@ -272,9 +272,9 @@ SMS_SENDER = 'GM'
|
||||||
|
|
||||||
# EMAIL
|
# EMAIL
|
||||||
EMAIL_USE_TLS = True
|
EMAIL_USE_TLS = True
|
||||||
EMAIL_HOST = 'smtp.gmail.com'
|
EMAIL_HOST = 'smtp.yandex.ru'
|
||||||
EMAIL_HOST_USER = 'anatolyfeteleu@gmail.com'
|
EMAIL_HOST_USER = 't3st.t3stov.t3stovich@yandex.ru'
|
||||||
EMAIL_HOST_PASSWORD = 'nggrlnbehzksgmbt'
|
EMAIL_HOST_PASSWORD = 'ylhernyutkfbylgk'
|
||||||
EMAIL_PORT = 587
|
EMAIL_PORT = 587
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -397,6 +397,7 @@ PASSWORD_RESET_TIMEOUT_DAYS = 1
|
||||||
RESETTING_TOKEN_TEMPLATE = 'account/password_reset_email.html'
|
RESETTING_TOKEN_TEMPLATE = 'account/password_reset_email.html'
|
||||||
CHANGE_EMAIL_TEMPLATE = 'account/change_email.html'
|
CHANGE_EMAIL_TEMPLATE = 'account/change_email.html'
|
||||||
CONFIRM_EMAIL_TEMPLATE = 'authorization/confirm_email.html'
|
CONFIRM_EMAIL_TEMPLATE = 'authorization/confirm_email.html'
|
||||||
|
NEWS_EMAIL_TEMPLATE = "news/news_email.html"
|
||||||
|
|
||||||
|
|
||||||
# COOKIES
|
# COOKIES
|
||||||
|
|
|
||||||
20
project/templates/news/news_email.html
Normal file
20
project/templates/news/news_email.html
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{ title }}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
|
||||||
|
{% if subtitle %}
|
||||||
|
<h3>{{ subtitle }}</h3>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<p>{{ description }} </p>
|
||||||
|
|
||||||
|
https://{{ country_code }}.{{ domain_uri }}{% url 'web:notification:unsubscribe' code %}
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@ urlpatterns = [
|
||||||
path('collections/', include('collection.urls.web')),
|
path('collections/', include('collection.urls.web')),
|
||||||
path('establishments/', include('establishment.urls.web')),
|
path('establishments/', include('establishment.urls.web')),
|
||||||
path('news/', include('news.urls.web')),
|
path('news/', include('news.urls.web')),
|
||||||
path('notifications/', include('notification.urls.web')),
|
path('notifications/', include(('notification.urls.web', "notification"), namespace='notification')),
|
||||||
path('partner/', include('partner.urls.web')),
|
path('partner/', include('partner.urls.web')),
|
||||||
path('location/', include('location.urls.web')),
|
path('location/', include('location.urls.web')),
|
||||||
path('main/', include('main.urls')),
|
path('main/', include('main.urls')),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user