Merge branch 'develop' into 'feature/bind-award-for-establishment'
# Conflicts: # apps/establishment/views/back.py
This commit is contained in:
commit
b778661564
|
|
@ -388,6 +388,7 @@ class User(PhoneModelMixin, AbstractUser):
|
||||||
'twitter_page_url': socials.twitter_page_url if socials else '#',
|
'twitter_page_url': socials.twitter_page_url if socials else '#',
|
||||||
'instagram_page_url': socials.instagram_page_url if socials else '#',
|
'instagram_page_url': socials.instagram_page_url if socials else '#',
|
||||||
'facebook_page_url': socials.facebook_page_url if socials else '#',
|
'facebook_page_url': socials.facebook_page_url if socials else '#',
|
||||||
|
'contact_email': socials.contact_email if socials else '-',
|
||||||
'send_to': username,
|
'send_to': username,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -430,6 +431,16 @@ class User(PhoneModelMixin, AbstractUser):
|
||||||
template_name=settings.EXISTING_USER_FOR_ESTABLISHMENT_TEAM_TEMPLATE,
|
template_name=settings.EXISTING_USER_FOR_ESTABLISHMENT_TEAM_TEMPLATE,
|
||||||
context=context), get_template(settings.EXISTING_USER_FOR_ESTABLISHMENT_TEAM_TEMPLATE).render(context)
|
context=context), get_template(settings.EXISTING_USER_FOR_ESTABLISHMENT_TEAM_TEMPLATE).render(context)
|
||||||
|
|
||||||
|
def establishment_team_role_revoked(self, country_code, username, subject, restaurant_name):
|
||||||
|
"""Template to notify user that his/her establishment role is revoked"""
|
||||||
|
context = {'token': self.reset_password_token,
|
||||||
|
'country_code': country_code,
|
||||||
|
'restaurant_name': restaurant_name}
|
||||||
|
context.update(self.base_template(country_code, username, subject))
|
||||||
|
return render_to_string(
|
||||||
|
template_name=settings.ESTABLISHMENT_TEAM_ROLE_REVOKED_TEMPLATE,
|
||||||
|
context=context), get_template(settings.ESTABLISHMENT_TEAM_ROLE_REVOKED_TEMPLATE).render(context)
|
||||||
|
|
||||||
def notify_password_changed_template(self, country_code, username, subject):
|
def notify_password_changed_template(self, country_code, username, subject):
|
||||||
"""Get notification email template"""
|
"""Get notification email template"""
|
||||||
context = {'country_code': country_code}
|
context = {'country_code': country_code}
|
||||||
|
|
|
||||||
|
|
@ -40,9 +40,15 @@ def send_team_invite_to_new_user(user_id, country_code, user_role_id, restaurant
|
||||||
subject = _(f'GAULT&MILLAU INVITES YOU TO MANAGE {restaurant_name}')
|
subject = _(f'GAULT&MILLAU INVITES YOU TO MANAGE {restaurant_name}')
|
||||||
message = user.invite_new_establishment_member_template(country_code, user.username,
|
message = user.invite_new_establishment_member_template(country_code, user.username,
|
||||||
subject, restaurant_name, user_role_id)
|
subject, restaurant_name, user_role_id)
|
||||||
user.send_email(subject=subject,
|
try:
|
||||||
message=message,
|
user.send_email(subject=subject,
|
||||||
emails=None)
|
message=message,
|
||||||
|
emails=None)
|
||||||
|
except:
|
||||||
|
cur_frame = inspect.currentframe()
|
||||||
|
cal_frame = inspect.getouterframes(cur_frame, 2)
|
||||||
|
logger.error(f'METHOD_NAME: {cal_frame[1][3]}\n'
|
||||||
|
f'DETAIL: Exception occurred for user: {user_id}')
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
|
|
@ -52,9 +58,33 @@ def send_team_invite_to_existing_user(user_id, country_code, user_role_id, resta
|
||||||
subject = _(f'GAULT&MILLAU INVITES YOU TO MANAGE {restaurant_name}')
|
subject = _(f'GAULT&MILLAU INVITES YOU TO MANAGE {restaurant_name}')
|
||||||
message = user.invite_establishment_member_template(country_code, user.username,
|
message = user.invite_establishment_member_template(country_code, user.username,
|
||||||
subject, restaurant_name, user_role_id)
|
subject, restaurant_name, user_role_id)
|
||||||
user.send_email(subject=subject,
|
try:
|
||||||
message=message,
|
user.send_email(subject=subject,
|
||||||
emails=None)
|
message=message,
|
||||||
|
emails=None)
|
||||||
|
except:
|
||||||
|
cur_frame = inspect.currentframe()
|
||||||
|
cal_frame = inspect.getouterframes(cur_frame, 2)
|
||||||
|
logger.error(f'METHOD_NAME: {cal_frame[1][3]}\n'
|
||||||
|
f'DETAIL: Exception occurred for user: {user_id}')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def team_role_revoked(user_id, country_code, restaurant_name):
|
||||||
|
"""Send email to establishment team member with role acceptance link"""
|
||||||
|
user = User.objects.get(id=user_id)
|
||||||
|
subject = _(f'Your GAULT&MILLAU privileges to manage {restaurant_name} have been revoked.')
|
||||||
|
message = user.establishment_team_role_revoked(country_code, user.username, subject, restaurant_name)
|
||||||
|
try:
|
||||||
|
user.send_email(subject=subject,
|
||||||
|
message=message,
|
||||||
|
emails=None)
|
||||||
|
except:
|
||||||
|
cur_frame = inspect.currentframe()
|
||||||
|
cal_frame = inspect.getouterframes(cur_frame, 2)
|
||||||
|
logger.error(f'METHOD_NAME: {cal_frame[1][3]}\n'
|
||||||
|
f'DETAIL: Exception occurred for user: {user_id}')
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ urlpatterns_api = [
|
||||||
path('reset-password/', views.PasswordResetView.as_view(), name='password-reset'),
|
path('reset-password/', views.PasswordResetView.as_view(), name='password-reset'),
|
||||||
path('reset-password/confirm/<uidb64>/<token>/', views.PasswordResetConfirmView.as_view(),
|
path('reset-password/confirm/<uidb64>/<token>/', views.PasswordResetConfirmView.as_view(),
|
||||||
name='password-reset-confirm'),
|
name='password-reset-confirm'),
|
||||||
|
path('join-establishment-team/<uidb64>/<token>/<int:role_id>', views.ApplyUserEstablishmentRole.as_view(),
|
||||||
|
name='join-establishment-team'),
|
||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = urlpatterns_api + \
|
urlpatterns = urlpatterns_api + \
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
"""Web account views"""
|
"""Web account views"""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.db import transaction
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.http import urlsafe_base64_decode
|
from django.utils.http import urlsafe_base64_decode
|
||||||
|
|
@ -61,10 +62,18 @@ class PasswordResetConfirmView(JWTGenericViewMixin, generics.GenericAPIView):
|
||||||
def patch(self, request, *args, **kwargs):
|
def patch(self, request, *args, **kwargs):
|
||||||
"""Implement PATCH method"""
|
"""Implement PATCH method"""
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
serializer = self.get_serializer(instance=instance,
|
with transaction.atomic():
|
||||||
data=request.data)
|
serializer = self.get_serializer(instance=instance,
|
||||||
serializer.is_valid(raise_exception=True)
|
data=request.data)
|
||||||
serializer.save()
|
serializer.is_valid(raise_exception=True)
|
||||||
|
serializer.save()
|
||||||
|
|
||||||
|
# apply requested user_role if 'role' in query_params
|
||||||
|
if 'role' in request.query_params:
|
||||||
|
user_role = get_object_or_404(klass=models.UserRole, pk=request.query_params['role'])
|
||||||
|
user_role.state = models.UserRole.VALIDATED
|
||||||
|
user_role.save()
|
||||||
|
|
||||||
# Create tokens
|
# Create tokens
|
||||||
tokens = instance.create_jwt_tokens()
|
tokens = instance.create_jwt_tokens()
|
||||||
return self._put_cookies_in_response(
|
return self._put_cookies_in_response(
|
||||||
|
|
@ -72,3 +81,40 @@ class PasswordResetConfirmView(JWTGenericViewMixin, generics.GenericAPIView):
|
||||||
access_token=tokens.get('access_token'),
|
access_token=tokens.get('access_token'),
|
||||||
refresh_token=tokens.get('refresh_token')),
|
refresh_token=tokens.get('refresh_token')),
|
||||||
response=Response(status=status.HTTP_200_OK))
|
response=Response(status=status.HTTP_200_OK))
|
||||||
|
|
||||||
|
|
||||||
|
class ApplyUserEstablishmentRole(JWTGenericViewMixin, generics.GenericAPIView):
|
||||||
|
|
||||||
|
queryset = models.User.objects.all()
|
||||||
|
permission_classes = (permissions.AllowAny,)
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
"""Overridden get_object method"""
|
||||||
|
queryset = self.filter_queryset(self.get_queryset())
|
||||||
|
uidb64 = self.kwargs.get('uidb64')
|
||||||
|
|
||||||
|
user_id = force_text(urlsafe_base64_decode(uidb64))
|
||||||
|
token = self.kwargs.get('token')
|
||||||
|
|
||||||
|
user = get_object_or_404(queryset, id=user_id)
|
||||||
|
|
||||||
|
if not GMTokenGenerator(GMTokenGenerator.RESET_PASSWORD).check_token(
|
||||||
|
user, token):
|
||||||
|
raise utils_exceptions.NotValidTokenError()
|
||||||
|
|
||||||
|
# May raise a permission denied
|
||||||
|
self.check_object_permissions(self.request, user)
|
||||||
|
|
||||||
|
return get_object_or_404(klass=models.UserRole, pk=self.kwargs['role_id'], user=user), user
|
||||||
|
|
||||||
|
def patch(self, request, *args, **kwargs):
|
||||||
|
instance, user = self.get_object()
|
||||||
|
instance.state = models.UserRole.VALIDATED
|
||||||
|
instance.save()
|
||||||
|
# Create tokens
|
||||||
|
tokens = user.create_jwt_tokens()
|
||||||
|
return self._put_cookies_in_response(
|
||||||
|
cookies=self._put_data_in_cookies(
|
||||||
|
access_token=tokens.get('access_token'),
|
||||||
|
refresh_token=tokens.get('refresh_token')),
|
||||||
|
response=Response(status=status.HTTP_200_OK))
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,18 @@ class GuideFilterSet(filters.FilterSet):
|
||||||
'Use for Establishment detail\'s sheet to content display within '
|
'Use for Establishment detail\'s sheet to content display within '
|
||||||
'"Collections & Guides" tab.'
|
'"Collections & Guides" tab.'
|
||||||
)
|
)
|
||||||
|
guide_type = filters.ChoiceFilter(
|
||||||
|
choices=models.Guide.GUIDE_TYPE_CHOICES,
|
||||||
|
help_text=f'It allows filtering guide list by type.'
|
||||||
|
f'Enum: {dict(models.Guide.GUIDE_TYPE_CHOICES)}'
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Meta class."""
|
"""Meta class."""
|
||||||
model = models.Guide
|
model = models.Guide
|
||||||
fields = (
|
fields = (
|
||||||
'establishment_id',
|
'establishment_id',
|
||||||
|
'guide_type',
|
||||||
)
|
)
|
||||||
|
|
||||||
def by_establishment_id(self, queryset, name, value):
|
def by_establishment_id(self, queryset, name, value):
|
||||||
|
|
|
||||||
|
|
@ -137,8 +137,8 @@ class GuideBaseSerializer(serializers.ModelSerializer):
|
||||||
'count_objects_during_init',
|
'count_objects_during_init',
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'guide_type': {'write_only': True},
|
'guide_type': {'write_only': True, 'required': True},
|
||||||
'site': {'write_only': True},
|
'site': {'write_only': True, 'required': True},
|
||||||
'state': {'write_only': True},
|
'state': {'write_only': True},
|
||||||
'start': {'required': True},
|
'start': {'required': True},
|
||||||
'slug': {'required': False},
|
'slug': {'required': False},
|
||||||
|
|
|
||||||
|
|
@ -9,28 +9,6 @@ logging.basicConfig(format='[%(levelname)s] %(message)s', level=logging.INFO)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_additional_establishment_data(section_node, establishment, guide):
|
|
||||||
data = {
|
|
||||||
'restaurant_section_node_id': section_node.id,
|
|
||||||
'guide_id': guide.id,
|
|
||||||
'establishment_id': establishment.id,
|
|
||||||
}
|
|
||||||
if establishment.last_published_review:
|
|
||||||
data.update({'review_id': establishment.last_published_review.id})
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def get_additional_product_data(section_node, product, guide):
|
|
||||||
data = {
|
|
||||||
'color_wine_section_node_id': section_node.id,
|
|
||||||
'wine_id': product.id,
|
|
||||||
'guide_id': guide.id,
|
|
||||||
}
|
|
||||||
if product.last_published_review:
|
|
||||||
data.update({'review_id': product.last_published_review.id})
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def generate_establishment_guide_elements(guide_id: int, filter_set: dict):
|
def generate_establishment_guide_elements(guide_id: int, filter_set: dict):
|
||||||
"""Generate guide elements."""
|
"""Generate guide elements."""
|
||||||
|
|
@ -38,9 +16,9 @@ def generate_establishment_guide_elements(guide_id: int, filter_set: dict):
|
||||||
from establishment.models import Establishment
|
from establishment.models import Establishment
|
||||||
|
|
||||||
guide = Guide.objects.get(id=guide_id)
|
guide = Guide.objects.get(id=guide_id)
|
||||||
guide.change_state(Guide.BUILDING)
|
|
||||||
queryset_values = Establishment.objects.filter(**filter_set).values()
|
queryset_values = Establishment.objects.filter(**filter_set).values()
|
||||||
try:
|
try:
|
||||||
|
guide.change_state(Guide.BUILDING)
|
||||||
for instance in queryset_values:
|
for instance in queryset_values:
|
||||||
populate_establishment_guide(guide_id, instance.get('id'))
|
populate_establishment_guide(guide_id, instance.get('id'))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -59,8 +37,8 @@ def populate_establishment_guide(guide_id: int, establishment_id: int):
|
||||||
from establishment.models import Establishment
|
from establishment.models import Establishment
|
||||||
|
|
||||||
guide = Guide.objects.get(id=guide_id)
|
guide = Guide.objects.get(id=guide_id)
|
||||||
guide.change_state(Guide.BUILDING)
|
|
||||||
try:
|
try:
|
||||||
|
guide.change_state(Guide.BUILDING)
|
||||||
establishment_qs = Establishment.objects.filter(id=establishment_id)
|
establishment_qs = Establishment.objects.filter(id=establishment_id)
|
||||||
if establishment_qs.exists():
|
if establishment_qs.exists():
|
||||||
establishment = establishment_qs.first()
|
establishment = establishment_qs.first()
|
||||||
|
|
@ -72,13 +50,17 @@ def populate_establishment_guide(guide_id: int, establishment_id: int):
|
||||||
section_node, _ = GuideElement.objects.get_or_create_establishment_section_node(
|
section_node, _ = GuideElement.objects.get_or_create_establishment_section_node(
|
||||||
city_node.id,
|
city_node.id,
|
||||||
transform_into_section_name(establishment.establishment_type.index_name),
|
transform_into_section_name(establishment.establishment_type.index_name),
|
||||||
guide_id,
|
guide.id,
|
||||||
)
|
)
|
||||||
if section_node:
|
if section_node:
|
||||||
GuideElement.objects.get_or_create_establishment_node(
|
params = {
|
||||||
**get_additional_establishment_data(section_node=section_node,
|
'restaurant_section_node_id': section_node.id,
|
||||||
establishment=establishment,
|
'guide_id': guide.id,
|
||||||
guide=guide))
|
'establishment_id': establishment.id,
|
||||||
|
}
|
||||||
|
if establishment.last_published_review:
|
||||||
|
params.update({'review_id': establishment.last_published_review.id})
|
||||||
|
GuideElement.objects.get_or_create_establishment_node(**params)
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
f'METHOD_NAME: {generate_establishment_guide_elements.__name__}\n'
|
||||||
|
|
@ -108,8 +90,8 @@ def remove_establishment_guide(guide_id: int, establishment_id: int):
|
||||||
from establishment.models import Establishment
|
from establishment.models import Establishment
|
||||||
|
|
||||||
guide = Guide.objects.get(id=guide_id)
|
guide = Guide.objects.get(id=guide_id)
|
||||||
guide.change_state(Guide.REMOVING)
|
|
||||||
try:
|
try:
|
||||||
|
guide.change_state(Guide.REMOVING)
|
||||||
establishment_qs = Establishment.objects.filter(id=establishment_id)
|
establishment_qs = Establishment.objects.filter(id=establishment_id)
|
||||||
if establishment_qs.exists():
|
if establishment_qs.exists():
|
||||||
establishment = establishment_qs.first()
|
establishment = establishment_qs.first()
|
||||||
|
|
@ -144,9 +126,9 @@ def generate_product_guide_elements(guide_id: int, filter_set: dict):
|
||||||
from product.models import Product
|
from product.models import Product
|
||||||
|
|
||||||
guide = Guide.objects.get(id=guide_id)
|
guide = Guide.objects.get(id=guide_id)
|
||||||
guide.change_state(Guide.BUILDING)
|
|
||||||
queryset_values = Product.objects.filter(**filter_set).values()
|
queryset_values = Product.objects.filter(**filter_set).values()
|
||||||
try:
|
try:
|
||||||
|
guide.change_state(Guide.BUILDING)
|
||||||
for instance in queryset_values:
|
for instance in queryset_values:
|
||||||
wine_id = instance.get('id')
|
wine_id = instance.get('id')
|
||||||
wine_qs = Product.objects.filter(id=wine_id)
|
wine_qs = Product.objects.filter(id=wine_id)
|
||||||
|
|
@ -157,26 +139,29 @@ def generate_product_guide_elements(guide_id: int, filter_set: dict):
|
||||||
wine_region_node, _ = GuideElement.objects.get_or_create_wine_region_node(
|
wine_region_node, _ = GuideElement.objects.get_or_create_wine_region_node(
|
||||||
root_node.id,
|
root_node.id,
|
||||||
wine.wine_region.id,
|
wine.wine_region.id,
|
||||||
guide_id)
|
guide.id)
|
||||||
if wine_region_node:
|
if wine_region_node:
|
||||||
yard_node, _ = GuideElement.objects.get_or_create_yard_node(
|
yard_node, _ = GuideElement.objects.get_or_create_yard_node(
|
||||||
wine.id,
|
wine.id,
|
||||||
wine_region_node.id,
|
wine_region_node.id,
|
||||||
guide_id)
|
guide.id)
|
||||||
if yard_node:
|
if yard_node:
|
||||||
wine_color_qs = wine.wine_colors
|
wine_color_qs = wine.wine_colors
|
||||||
if wine_color_qs.exists():
|
if wine_color_qs.exists():
|
||||||
wine_color_section, _ = GuideElement.objects.get_or_create_color_wine_section_node(
|
wine_color_section, _ = GuideElement.objects.get_or_create_color_wine_section_node(
|
||||||
wine_color_qs.first().value,
|
wine_color_qs.first().value,
|
||||||
yard_node.id,
|
yard_node.id,
|
||||||
guide_id
|
guide.id
|
||||||
)
|
)
|
||||||
if wine_color_section:
|
if wine_color_section:
|
||||||
GuideElement.objects.get_or_create_wine_node(
|
params = {
|
||||||
**get_additional_product_data(
|
'color_wine_section_node_id': wine_color_section.id,
|
||||||
wine_color_section,
|
'wine_id': wine.id,
|
||||||
wine,
|
'guide_id': guide.id,
|
||||||
guide))
|
}
|
||||||
|
if wine.last_published_review:
|
||||||
|
params.update({'review_id': wine.last_published_review.id})
|
||||||
|
GuideElement.objects.get_or_create_wine_node(**params)
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
f'METHOD_NAME: {generate_product_guide_elements.__name__}\n'
|
||||||
|
|
|
||||||
|
|
@ -100,9 +100,48 @@ class CollectionBackOfficeList(CollectionBackOfficeViewSet):
|
||||||
pagination_class = None
|
pagination_class = None
|
||||||
|
|
||||||
|
|
||||||
class GuideListCreateView(GuideBaseView,
|
class GuideListCreateView(GuideBaseView, generics.ListCreateAPIView):
|
||||||
generics.ListCreateAPIView):
|
"""
|
||||||
"""View for Guides list for BackOffice users and Guide create."""
|
## Guide list/create view.
|
||||||
|
### Request
|
||||||
|
For creating new instance of Guide model, need to pass the following data in the request:
|
||||||
|
required fields:
|
||||||
|
* name (str) - guide name
|
||||||
|
* start (str) - guide start date (datetime in a format `ISO-8601`)
|
||||||
|
* vintage (str) - valid year
|
||||||
|
* guide_type (int) - guide type enum: `0 (Restaurant), 1 (Artisan), 2 (Wine)`
|
||||||
|
* site (int) - identifier of site
|
||||||
|
non-required fields:
|
||||||
|
* slug - generated automatically if not provided
|
||||||
|
* state - state enum`: `0 (Built), 1 (Waiting), 2 (Removing), 3 (Building)` (service states)
|
||||||
|
* end - guide end date (datetime in a format `ISO-8601`)
|
||||||
|
|
||||||
|
### Response
|
||||||
|
Return paginated list of guides.
|
||||||
|
I.e.:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"count": 58,
|
||||||
|
"next": 2,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Description
|
||||||
|
*GET*
|
||||||
|
Return paginated list of guides with the opportunity of filtering by next fields:
|
||||||
|
* establishment_id (int) - identifier of establishment,
|
||||||
|
* guide_type (int) - guide type enum: `0 (Restaurant), 1 (Artisan), 2 (Wine)`
|
||||||
|
|
||||||
|
*POST*
|
||||||
|
Create a new instance of guide.
|
||||||
|
"""
|
||||||
filter_class = filters.GuideFilterSet
|
filter_class = filters.GuideFilterSet
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
"""Establishment app views."""
|
"""Establishment app views."""
|
||||||
|
from django.conf import settings
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models.query_utils import Q
|
from django.db.models.query_utils import Q
|
||||||
from django.http import Http404
|
from django.http import Http404
|
||||||
|
|
@ -941,24 +942,11 @@ class TeamMemberDeleteView(generics.DestroyAPIView):
|
||||||
return UserRole.objects.get(role__role=Role.ESTABLISHMENT_ADMINISTRATOR, user_id=self.kwargs['user_id'],
|
return UserRole.objects.get(role__role=Role.ESTABLISHMENT_ADMINISTRATOR, user_id=self.kwargs['user_id'],
|
||||||
establishment_id=self.kwargs['establishment_id'])
|
establishment_id=self.kwargs['establishment_id'])
|
||||||
|
|
||||||
|
def perform_destroy(self, instance):
|
||||||
class EstablishmentAwardCreateAndBind(generics.CreateAPIView, generics.DestroyAPIView):
|
instance.delete()
|
||||||
queryset = main_models.Award.objects.with_base_related().all()
|
from account.tasks import team_role_revoked
|
||||||
permission_classes = get_permission_classes()
|
establishment = models.Establishment.objects.get(pk=self.kwargs['establishment_id'])
|
||||||
serializer_class = serializers.BackEstablishmentAwardCreateSerializer
|
if settings.USE_CELERY:
|
||||||
|
team_role_revoked.delay(self.kwargs['user_id'], self.request.country_code, establishment.name)
|
||||||
def _award_list_for_establishment(self, establishment_id: int, status: int) -> Response:
|
else:
|
||||||
awards = main_models.Award.objects.with_base_related().filter(object_id=establishment_id)
|
team_role_revoked(self.kwargs['user_id'], self.request.country_code, establishment.name)
|
||||||
response_serializer = main_serializers.AwardBaseSerializer(awards, many=True)
|
|
||||||
headers = self.get_success_headers(response_serializer.data)
|
|
||||||
return Response(response_serializer.data, status=status, headers=headers)
|
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
|
||||||
"""Overridden create method."""
|
|
||||||
super().create(request, args, kwargs)
|
|
||||||
return self._award_list_for_establishment(kwargs['establishment_id'], status.HTTP_201_CREATED)
|
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
|
||||||
establishment = get_object_or_404(models.Establishment, id=kwargs['establishment_id'])
|
|
||||||
establishment.remove_award(kwargs['award_id'])
|
|
||||||
return self._award_list_for_establishment(kwargs['establishment_id'], status.HTTP_200_OK)
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -247,19 +247,19 @@ def get_ruby_data():
|
||||||
|
|
||||||
ruby_params = {}
|
ruby_params = {}
|
||||||
|
|
||||||
if mysql_city.country_code is not None:
|
if mysql_city.country_code:
|
||||||
ruby_params['country_code'] = mysql_city.country_code
|
ruby_params['country_code'] = mysql_city.country_code
|
||||||
|
|
||||||
if mysql_city.country_code_2 is not None:
|
if mysql_city.country_code_2:
|
||||||
ruby_params['country_code_2'] = mysql_city.country_code_2
|
ruby_params['country_code_2'] = mysql_city.country_code_2
|
||||||
|
|
||||||
if mysql_city.region_code is not None:
|
if mysql_city.region_code:
|
||||||
ruby_params['region_code'] = mysql_city.region_code
|
ruby_params['region_code'] = mysql_city.region_code
|
||||||
|
|
||||||
if mysql_city.subregion_code is not None:
|
if mysql_city.subregion_code:
|
||||||
ruby_params['subregion_code'] = mysql_city.subregion_code
|
ruby_params['subregion_code'] = mysql_city.subregion_code
|
||||||
|
|
||||||
ruby_response = get_ruby_socket(ruby_params)
|
ruby_response = get_ruby_socket(ruby_params) if ruby_params else None
|
||||||
|
|
||||||
if ruby_response is None:
|
if ruby_response is None:
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -455,6 +455,7 @@ PASSWORD_RESET_TIMEOUT_DAYS = 1
|
||||||
RESETTING_TOKEN_TEMPLATE = 'account/password_reset_email.html'
|
RESETTING_TOKEN_TEMPLATE = 'account/password_reset_email.html'
|
||||||
NEW_USER_FOR_ESTABLISHMENT_TEAM_TEMPLATE = 'account/invite_est_team_new_user.html'
|
NEW_USER_FOR_ESTABLISHMENT_TEAM_TEMPLATE = 'account/invite_est_team_new_user.html'
|
||||||
EXISTING_USER_FOR_ESTABLISHMENT_TEAM_TEMPLATE = 'account/invite_est_team_existing_user.html'
|
EXISTING_USER_FOR_ESTABLISHMENT_TEAM_TEMPLATE = 'account/invite_est_team_existing_user.html'
|
||||||
|
ESTABLISHMENT_TEAM_ROLE_REVOKED_TEMPLATE = 'account/est_team_role_revoked.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'
|
NEWS_EMAIL_TEMPLATE = 'news/news_email.html'
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
"""Development settings."""
|
"""Development settings."""
|
||||||
from .base import *
|
|
||||||
from .amazon_s3 import *
|
from .amazon_s3 import *
|
||||||
|
from .base import *
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['gm.id-east.ru', '95.213.204.126', '0.0.0.0']
|
ALLOWED_HOSTS = ['gm.id-east.ru', '95.213.204.126', '0.0.0.0']
|
||||||
|
|
||||||
|
|
@ -77,3 +77,33 @@ EMAIL_PORT = 587
|
||||||
|
|
||||||
|
|
||||||
MIDDLEWARE.append('utils.middleware.log_db_queries_per_API_request')
|
MIDDLEWARE.append('utils.middleware.log_db_queries_per_API_request')
|
||||||
|
|
||||||
|
LOGGING = {
|
||||||
|
'version': 1,
|
||||||
|
'disable_existing_loggers': False,
|
||||||
|
'filters': {
|
||||||
|
'require_debug_false': {
|
||||||
|
'()': 'django.utils.log.RequireDebugFalse',
|
||||||
|
},
|
||||||
|
'require_debug_true': {
|
||||||
|
'()': 'django.utils.log.RequireDebugTrue',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'handlers': {
|
||||||
|
'console': {
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'filters': ['require_debug_true'],
|
||||||
|
'class': 'logging.StreamHandler',
|
||||||
|
},
|
||||||
|
'null': {
|
||||||
|
'class': 'logging.NullHandler',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'loggers': {
|
||||||
|
'django.db.backends': {
|
||||||
|
'handlers': ['console', ],
|
||||||
|
'level': 'ERROR',
|
||||||
|
'propagate': False,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,11 +86,11 @@ LOGGING = {
|
||||||
'py.warnings': {
|
'py.warnings': {
|
||||||
'handlers': ['console'],
|
'handlers': ['console'],
|
||||||
},
|
},
|
||||||
# 'django.db.backends': {
|
'django.db.backends': {
|
||||||
# 'handlers': ['console', ],
|
'handlers': ['console', ],
|
||||||
# 'level': 'DEBUG',
|
'level': 'DEBUG',
|
||||||
# 'propagate': False,
|
'propagate': False,
|
||||||
# },
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
76
project/templates/account/est_team_role_revoked.html
Normal file
76
project/templates/account/est_team_role_revoked.html
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
{% load i18n %}{% autoescape off %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" style="box-sizing: border-box;margin: 0;">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700|PT+Serif&display=swap&subset=cyrillic" type="text/css">
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body style="box-sizing: border-box;margin: 0;font-family: "Open Sans", sans-serif;font-size: 0.875rem;">
|
||||||
|
<div style="dispaly: none">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin: 0 auto; max-width:38.25rem;" class="letter">
|
||||||
|
<div class="letter__wrapper">
|
||||||
|
<div class="letter__inner">
|
||||||
|
<div class="letter__content" style="position: relative;margin: 0 16px 40px;padding: 0 0 1px;">
|
||||||
|
<div class="letter__header" style="margin: 1.875rem 0 2.875rem;text-align: center;">
|
||||||
|
<div class="letter__logo" style="display: block;width: 7.9375rem;height: 4.6875rem;margin: 0 auto 14px auto;">
|
||||||
|
<a href="https://{{ country_code }}.{{ domain_uri }}/" class="letter__logo-photo" style="color: #000;font-weight: 700;text-decoration: none;padding: 0;border-bottom: 1.5px solid #ffee29;cursor: pointer;display: block;border: none;">
|
||||||
|
<img alt="" style="width:100%;" src="https://s3.eu-central-1.amazonaws.com/gm-test.com/manually_uploaded/1.png" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="letter__sublogo" style="font-size: 21px;line-height: 1;letter-spacing: 0;color: #bcbcbc;text-transform: uppercase;">france</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="letter__title" style="font-family:"Open-Sans",sans-serif; font-size: 1.5rem;margin: 0 0 10px;padding: 0 0 6px;border-bottom: 4px solid #ffee29;">
|
||||||
|
<span class="letter__title-txt">{{ title }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="letter__text" style="margin: 0 0 30px; font-family:"Open-Sans",sans-serif; font-size: 14px; line-height: 21px;letter-spacing: -0.34px; overflow-x: hidden;">
|
||||||
|
<br>
|
||||||
|
{% blocktrans %}Hello, you are receiving this email because your admin privileges on <b>{{ restaurant_name }}</b> have been revoked by another administrator.
|
||||||
|
If you think this is a mistake, you should contact {{ contact_email }}.{% endblocktrans %}
|
||||||
|
<br>
|
||||||
|
<br>
|
||||||
|
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
|
||||||
|
</div>
|
||||||
|
<div class="letter__follow" style="padding: 8px;margin: 0 auto 40px auto;background: #ffee29; max-width: 400px;">
|
||||||
|
<div class="letter__follow-content" style="padding: 1.25rem 0;background: #fff;text-align: center;">
|
||||||
|
<div class="letter__follow-header" style="display: inline-block;margin: 0 0 18px;font-family: "PT Serif", sans-serif;font-size: 1.25rem;text-transform: uppercase;">
|
||||||
|
<img alt="thumb" style="width:30px;vertical-align: sub; display: inline-block" src="https://s3.eu-central-1.amazonaws.com/gm-test.com/manually_uploaded/2.png" />
|
||||||
|
<span class="letter__follow-title">Follow us</span>
|
||||||
|
</div>
|
||||||
|
<div class="letter__follow-text" style="display: block;margin: 0 0 30px;font-size: 12px;font-style: italic;">You can also find us on our social network below
|
||||||
|
</div>
|
||||||
|
<div class="letter__follow-social">
|
||||||
|
<a href="{{ facebook_page_url }}" class="letter__follow-link" target="_blank" style="color: #000;font-weight: 700;text-decoration: none;padding: 0;border-bottom: 1.5px solid #ffee29;cursor: pointer;display: inline-block;width: 30px;height: 30px;margin: 0 1rem 0 0;background: #ffee29;border: none;">
|
||||||
|
<img alt="facebook" style="width: 30px; vertical-align: sub; display: inline-block" src="https://s3.eu-central-1.amazonaws.com/gm-test.com/manually_uploaded/3.png" />
|
||||||
|
</a>
|
||||||
|
<a href="{{ instagram_page_url }}" class="letter__follow-link" target="_blank" style="color: #000;font-weight: 700;text-decoration: none;padding: 0;border-bottom: 1.5px solid #ffee29;cursor: pointer;display: inline-block;width: 30px;height: 30px;margin: 0 1rem 0 0;background: #ffee29;border: none;">
|
||||||
|
<img alt="instagram" style="width:30px;vertical-align: sub; display: inline-block" src="https://s3.eu-central-1.amazonaws.com/gm-test.com/manually_uploaded/4.png" />
|
||||||
|
</a>
|
||||||
|
<a href="{{ twitter_page_url }}" class="letter__follow-link" target="_blank" style="color: #000;font-weight: 700;text-decoration: none;padding: 0;border-bottom: 1.5px solid #ffee29;cursor: pointer;display: inline-block;width: 30px;height: 30px;margin: 0;background: #ffee29;border: none;">
|
||||||
|
<img alt="twitter" style="width:30px;vertical-align: sub; display: inline-block" src="https://s3.eu-central-1.amazonaws.com/gm-test.com/manually_uploaded/5.png" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="letter__unsubscribe" style="margin: 0 0 1.25rem;font-size: 12px;text-align: center;">
|
||||||
|
<span class="letter__unsubscribe-dscr" style="display: inline-block;">Please contact {{ contact_email }} if you have any questions.</span>
|
||||||
|
</div>
|
||||||
|
<div class="letter__footer" style="padding: 24px 0 15px;text-align: center;background: #ffee29;">
|
||||||
|
<div class="letter__footer-logo" style="width: 71px;height: 42px;margin: 0 auto 14px auto;">
|
||||||
|
<a href="#" class="letter__footer-logo-photo" style="color: #000;font-weight: 700;text-decoration: none;padding: 0;border-bottom: 1.5px solid #ffee29;cursor: pointer;">
|
||||||
|
<img alt="" style="width: 100%;" src="https://s3.eu-central-1.amazonaws.com/gm-test.com/manually_uploaded/6.png" /></a>
|
||||||
|
</div>
|
||||||
|
<div class="letter__copyright">GaultMillau © {{ year }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{% endautoescape %}
|
||||||
|
|
@ -31,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="letter__text" style="margin: 0 0 30px; font-family:"Open-Sans",sans-serif; font-size: 14px; line-height: 21px;letter-spacing: -0.34px; overflow-x: hidden;">
|
<div class="letter__text" style="margin: 0 0 30px; font-family:"Open-Sans",sans-serif; font-size: 14px; line-height: 21px;letter-spacing: -0.34px; overflow-x: hidden;">
|
||||||
<br>
|
<br>
|
||||||
{% blocktrans %}Hi, you are receiving this email because you have been granted to manage the establishment <b>%restaurant_name%</b>.
|
{% blocktrans %}Hi, you are receiving this email because you have been granted to manage the establishment <b>{{ restaurant_name }}</b>.
|
||||||
From now, you can manage information about this place and have it published in {{ site_name }}.{% endblocktrans %}
|
From now, you can manage information about this place and have it published in {{ site_name }}.{% endblocktrans %}
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
@ -66,7 +66,7 @@ From now, you can manage information about this place and have it published in {
|
||||||
<div class="letter__unsubscribe" style="margin: 0 0 1.25rem;font-size: 12px;text-align: center;">
|
<div class="letter__unsubscribe" style="margin: 0 0 1.25rem;font-size: 12px;text-align: center;">
|
||||||
<span class="letter__unsubscribe-dscr" style="display: inline-block;"> In case that you were not already a Gault&Millau user, a temporary account with your email has been temporarily created by a Gault&Millau administrator.
|
<span class="letter__unsubscribe-dscr" style="display: inline-block;"> In case that you were not already a Gault&Millau user, a temporary account with your email has been temporarily created by a Gault&Millau administrator.
|
||||||
By completing the creation process of your account, you agree to have this account permanently created.
|
By completing the creation process of your account, you agree to have this account permanently created.
|
||||||
This temporary account will be deleted after 7 days if you don't complete the registration process. Please contact %license_contact_email% if you have any questions.</span>
|
This temporary account will be deleted after 7 days if you don't complete the registration process. Please contact {{ contact_email }} if you have any questions.</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="letter__footer" style="padding: 24px 0 15px;text-align: center;background: #ffee29;">
|
<div class="letter__footer" style="padding: 24px 0 15px;text-align: center;background: #ffee29;">
|
||||||
<div class="letter__footer-logo" style="width: 71px;height: 42px;margin: 0 auto 14px auto;">
|
<div class="letter__footer-logo" style="width: 71px;height: 42px;margin: 0 auto 14px auto;">
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ From now, you can manage information about this place and have it published in {
|
||||||
<div class="letter__unsubscribe" style="margin: 0 0 1.25rem;font-size: 12px;text-align: center;">
|
<div class="letter__unsubscribe" style="margin: 0 0 1.25rem;font-size: 12px;text-align: center;">
|
||||||
<span class="letter__unsubscribe-dscr" style="display: inline-block;">In case that you were not already a Gault&Millau user, a temporary account with your email has been temporarily created by a Gault&Millau administrator.
|
<span class="letter__unsubscribe-dscr" style="display: inline-block;">In case that you were not already a Gault&Millau user, a temporary account with your email has been temporarily created by a Gault&Millau administrator.
|
||||||
By completing the creation process of your account, you agree to have this account permanently created.
|
By completing the creation process of your account, you agree to have this account permanently created.
|
||||||
This temporary account will be deleted after 7 days if you don't complete the registration process. Please contact %license_contact_email% if you have any questions. </span>
|
This temporary account will be deleted after 7 days if you don't complete the registration process. Please contact {{ contact_email }} if you have any questions. </span>
|
||||||
</div>
|
</div>
|
||||||
<div class="letter__footer" style="padding: 24px 0 15px;text-align: center;background: #ffee29;">
|
<div class="letter__footer" style="padding: 24px 0 15px;text-align: center;background: #ffee29;">
|
||||||
<div class="letter__footer-logo" style="width: 71px;height: 42px;margin: 0 auto 14px auto;">
|
<div class="letter__footer-logo" style="width: 71px;height: 42px;margin: 0 auto 14px auto;">
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user