Merge branch 'develop' of ssh://gl.id-east.ru:222/gm/gm-backend into develop
This commit is contained in:
commit
bb040fffdf
|
|
@ -118,7 +118,7 @@ class GuideListCreateView(GuideBaseView, generics.ListCreateAPIView):
|
||||||
|
|
||||||
### Response
|
### Response
|
||||||
Return paginated list of guides.
|
Return paginated list of guides.
|
||||||
I.e.:
|
E.g.:
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"count": 58,
|
"count": 58,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@ from django.core.exceptions import ValidationError
|
||||||
from django.core.validators import MaxValueValidator, MinValueValidator
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Case, ExpressionWrapper, F, Prefetch, Q, Subquery, When
|
from django.db.models import Case, ExpressionWrapper, F, Prefetch, Q, Subquery, When
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from phonenumber_field.modelfields import PhoneNumberField
|
from phonenumber_field.modelfields import PhoneNumberField
|
||||||
|
|
@ -679,7 +678,7 @@ class Establishment(GalleryMixin, ProjectBaseMixin, URLImageMixin,
|
||||||
@property
|
@property
|
||||||
def visible_tags_detail(self):
|
def visible_tags_detail(self):
|
||||||
"""Removes some tags from detail Establishment representation"""
|
"""Removes some tags from detail Establishment representation"""
|
||||||
return self.visible_tags.exclude(category__index_name__in=['tag'])
|
return self.visible_tags.exclude(category__index_name__in=['tag', 'shop_category'])
|
||||||
|
|
||||||
def recalculate_public_mark(self):
|
def recalculate_public_mark(self):
|
||||||
fresh_review = self.reviews.published().order_by('-modified').first()
|
fresh_review = self.reviews.published().order_by('-modified').first()
|
||||||
|
|
@ -1145,7 +1144,7 @@ class EmployeeQuerySet(models.QuerySet):
|
||||||
queryset=EstablishmentEmployee.objects.actual()
|
queryset=EstablishmentEmployee.objects.actual()
|
||||||
.prefetch_related('establishment', 'position').order_by('-from_date'),
|
.prefetch_related('establishment', 'position').order_by('-from_date'),
|
||||||
to_attr='prefetched_establishment_employee'),
|
to_attr='prefetched_establishment_employee'),
|
||||||
'awards'
|
Prefetch('awards', queryset=Award.objects.select_related('award_type'))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
|
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.db.models import F
|
from django.db.models import F
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
@ -14,7 +15,8 @@ from establishment import models, serializers as model_serializers
|
||||||
from establishment.models import ContactEmail, ContactPhone, EstablishmentEmployee
|
from establishment.models import ContactEmail, ContactPhone, EstablishmentEmployee
|
||||||
from establishment.serializers.common import ContactPhonesSerializer
|
from establishment.serializers.common import ContactPhonesSerializer
|
||||||
from gallery.models import Image
|
from gallery.models import Image
|
||||||
from location.serializers import AddressDetailSerializer, TranslatedField, AddressBaseSerializer
|
from location.serializers import AddressDetailSerializer, TranslatedField
|
||||||
|
from main import models as main_models
|
||||||
from main.models import Currency
|
from main.models import Currency
|
||||||
from main.serializers import AwardSerializer
|
from main.serializers import AwardSerializer
|
||||||
from tag.serializers import TagBaseSerializer
|
from tag.serializers import TagBaseSerializer
|
||||||
|
|
@ -22,8 +24,6 @@ from utils.decorators import with_base_attributes
|
||||||
from utils.methods import string_random
|
from utils.methods import string_random
|
||||||
from utils.serializers import ImageBaseSerializer, ProjectModelSerializer, TimeZoneChoiceField, \
|
from utils.serializers import ImageBaseSerializer, ProjectModelSerializer, TimeZoneChoiceField, \
|
||||||
PhoneMixinSerializer
|
PhoneMixinSerializer
|
||||||
from main import models as main_models
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
|
|
||||||
|
|
||||||
def phones_handler(phones_list, establishment):
|
def phones_handler(phones_list, establishment):
|
||||||
|
|
@ -59,7 +59,7 @@ class EstablishmentListCreateSerializer(model_serializers.EstablishmentBaseSeria
|
||||||
queryset=models.Address.objects.all(),
|
queryset=models.Address.objects.all(),
|
||||||
write_only=True
|
write_only=True
|
||||||
)
|
)
|
||||||
address = AddressBaseSerializer(read_only=True, allow_null=True)
|
address = AddressDetailSerializer(read_only=True, allow_null=True)
|
||||||
transliterated_name = serializers.CharField(
|
transliterated_name = serializers.CharField(
|
||||||
required=False, allow_null=True, allow_blank=True
|
required=False, allow_null=True, allow_blank=True
|
||||||
)
|
)
|
||||||
|
|
@ -363,7 +363,10 @@ class EmployeeBackSerializers(PhoneMixinSerializer, serializers.ModelSerializer)
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@lru_cache(maxsize=32)
|
@lru_cache(maxsize=32)
|
||||||
def get_qs(obj):
|
def get_qs(obj):
|
||||||
return obj.establishmentemployee_set.actual().annotate(
|
return obj.establishmentemployee_set.actual().only(
|
||||||
|
'establishment',
|
||||||
|
'from_date',
|
||||||
|
).annotate(
|
||||||
public_mark=F('establishment__public_mark'),
|
public_mark=F('establishment__public_mark'),
|
||||||
est_id=F('establishment__id'),
|
est_id=F('establishment__id'),
|
||||||
est_slug=F('establishment__slug'),
|
est_slug=F('establishment__slug'),
|
||||||
|
|
@ -526,10 +529,8 @@ class EstEmployeeBackSerializer(EmployeeBackSerializers):
|
||||||
'toque_number',
|
'toque_number',
|
||||||
'available_for_events',
|
'available_for_events',
|
||||||
'photo',
|
'photo',
|
||||||
|
'photo_id',
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
|
||||||
'phone': {'write_only': True}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class EstablishmentBackOfficeGallerySerializer(serializers.ModelSerializer):
|
class EstablishmentBackOfficeGallerySerializer(serializers.ModelSerializer):
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ urlpatterns = [
|
||||||
path('employees/search/', views.EmployeesListSearchViews.as_view(), name='employees-search'),
|
path('employees/search/', views.EmployeesListSearchViews.as_view(), name='employees-search'),
|
||||||
path('employees/<int:pk>/', views.EmployeeRUDView.as_view(), name='employees-rud'),
|
path('employees/<int:pk>/', views.EmployeeRUDView.as_view(), name='employees-rud'),
|
||||||
path('employees/<int:pk>/<int:award_id>', views.RemoveAwardView.as_view(), name='employees-award-delete'),
|
path('employees/<int:pk>/<int:award_id>', views.RemoveAwardView.as_view(), name='employees-award-delete'),
|
||||||
path('<int:establishment_id>/employee/<int:employee_id>/position/<int:position_id>',
|
path('<int:establishment_id>/employee/<int:employee_id>/position/<int:position_id>/',
|
||||||
views.EstablishmentEmployeeCreateView.as_view(),
|
views.EstablishmentEmployeeCreateView.as_view(),
|
||||||
name='employees-establishment-create'),
|
name='employees-establishment-create'),
|
||||||
path('employee/position/<int:pk>/delete/', views.EstablishmentEmployeeDeleteView.as_view(),
|
path('employee/position/<int:pk>/delete/', views.EstablishmentEmployeeDeleteView.as_view(),
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAP
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super().get_queryset() \
|
return super().get_queryset() \
|
||||||
|
.with_extended_address_related() \
|
||||||
.with_certain_tag_category_related('category', 'restaurant_category') \
|
.with_certain_tag_category_related('category', 'restaurant_category') \
|
||||||
.with_certain_tag_category_related('cuisine', 'restaurant_cuisine') \
|
.with_certain_tag_category_related('cuisine', 'restaurant_cuisine') \
|
||||||
.with_certain_tag_category_related('shop_category', 'artisan_category') \
|
.with_certain_tag_category_related('shop_category', 'artisan_category') \
|
||||||
|
|
@ -68,8 +69,28 @@ class EstablishmentListCreateView(EstablishmentMixinViews, generics.ListCreateAP
|
||||||
|
|
||||||
|
|
||||||
class EmployeeEstablishmentPositionsView(generics.ListAPIView):
|
class EmployeeEstablishmentPositionsView(generics.ListAPIView):
|
||||||
"""Establishment by employee view."""
|
"""
|
||||||
|
## Establishment employee positions filtered by employee identifier.
|
||||||
|
### *GET*
|
||||||
|
#### Description
|
||||||
|
Return paginated list of results from an intermediate table filtered by employee
|
||||||
|
identifier, that contains connection between employee establishment,
|
||||||
|
employee hiring dates, position, status `'I' (Idle)`, `'A' (Accepted)`, `'D' (Declined)`.
|
||||||
|
##### Response
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"count": 58,
|
||||||
|
"next": 2,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
"""
|
||||||
queryset = models.EstablishmentEmployee.objects.all()
|
queryset = models.EstablishmentEmployee.objects.all()
|
||||||
serializer_class = serializers.EstablishmentEmployeePositionsSerializer
|
serializer_class = serializers.EstablishmentEmployeePositionsSerializer
|
||||||
permission_classes = get_permission_classes(
|
permission_classes = get_permission_classes(
|
||||||
|
|
@ -84,7 +105,26 @@ class EmployeeEstablishmentPositionsView(generics.ListAPIView):
|
||||||
|
|
||||||
|
|
||||||
class EmployeeEstablishmentsListView(generics.ListAPIView):
|
class EmployeeEstablishmentsListView(generics.ListAPIView):
|
||||||
"""Establishment by employee list view."""
|
"""
|
||||||
|
## Employee establishments filtered by employee identifier.
|
||||||
|
### *GET*
|
||||||
|
#### Description
|
||||||
|
Return paginated list of establishments filtered by employee identifier.
|
||||||
|
##### Response
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"count": 58,
|
||||||
|
"next": 2,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
"""
|
||||||
serializer_class = serializers.EstablishmentListCreateSerializer
|
serializer_class = serializers.EstablishmentListCreateSerializer
|
||||||
permission_classes = get_permission_classes(
|
permission_classes = get_permission_classes(
|
||||||
IsEstablishmentManager,
|
IsEstablishmentManager,
|
||||||
|
|
@ -98,8 +138,25 @@ class EmployeeEstablishmentsListView(generics.ListAPIView):
|
||||||
|
|
||||||
|
|
||||||
class EmployeePositionsListView(generics.ListAPIView):
|
class EmployeePositionsListView(generics.ListAPIView):
|
||||||
"""Establishment position by employee list view."""
|
"""
|
||||||
|
## Paginated list of establishments filtered by employee identifier
|
||||||
|
### *GET*
|
||||||
|
#### Description
|
||||||
|
Return a paginated list of establishments of an employee by employee identifier.
|
||||||
|
##### Response
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"count": 2,
|
||||||
|
"next": null,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
"""
|
||||||
queryset = models.Establishment.objects.all()
|
queryset = models.Establishment.objects.all()
|
||||||
serializer_class = serializers.EstablishmentPositionListSerializer
|
serializer_class = serializers.EstablishmentPositionListSerializer
|
||||||
permission_classes = get_permission_classes(
|
permission_classes = get_permission_classes(
|
||||||
|
|
@ -500,7 +557,7 @@ class EmployeeListCreateView(generics.ListCreateAPIView):
|
||||||
* position_id (`int`) - filter by employees position identifier
|
* position_id (`int`) - filter by employees position identifier
|
||||||
* public_mark (`str`) - filter by establishment public mark
|
* public_mark (`str`) - filter by establishment public mark
|
||||||
* toque_number (`str`) - filter by establishment toque number
|
* toque_number (`str`) - filter by establishment toque number
|
||||||
* username (`str`) - filter by username or name
|
* username (`str`) - filter by a username or name
|
||||||
|
|
||||||
#### Response
|
#### Response
|
||||||
```
|
```
|
||||||
|
|
@ -530,17 +587,18 @@ class EmployeeListCreateView(generics.ListCreateAPIView):
|
||||||
|
|
||||||
#### Request
|
#### Request
|
||||||
Required fields:
|
Required fields:
|
||||||
* available_for_events (bool) - flag that responds for availability for events
|
* name (`str`) - employee name
|
||||||
|
|
||||||
Non-required fields:
|
Non-required fields:
|
||||||
* name (`str`) - name
|
* name (`str`) - employee name
|
||||||
* last_name (`str`) - last name
|
* last_name (`str`) - employee last name
|
||||||
* user (`int`) - user identifier
|
* user (`int`) - user identifier
|
||||||
* sex (`int`) - enum: `0 (Male), 1 (Female)`
|
* sex (`int`) - enum: `0 (Male), 1 (Female)`
|
||||||
* birth_date (`str`) - birth datetime (datetime in a format `ISO-8601`)
|
* birth_date (`str`) - birth datetime (datetime in a format `ISO-8601`)
|
||||||
* email (`str`) - email address
|
* email (`str`) - email address
|
||||||
* phone (`str`) - phone number in format `E164`
|
* phone (`str`) - phone number in a format `E164`
|
||||||
* photo_id (`int`) - photo identifier
|
* photo_id (`int`) - photo identifier
|
||||||
|
* available_for_events (bool) - flag that responds for availability for events
|
||||||
"""
|
"""
|
||||||
filter_class = filters.EmployeeBackFilter
|
filter_class = filters.EmployeeBackFilter
|
||||||
serializer_class = serializers.EmployeeBackSerializers
|
serializer_class = serializers.EmployeeBackSerializers
|
||||||
|
|
@ -550,13 +608,44 @@ class EmployeeListCreateView(generics.ListCreateAPIView):
|
||||||
IsEstablishmentAdministrator,
|
IsEstablishmentAdministrator,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
qs = super().get_queryset()
|
||||||
|
if self.request.country_code:
|
||||||
|
qs = qs.filter(establishments__address__city__country__code=self.request.country_code)
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
class EmployeesListSearchViews(generics.ListAPIView):
|
class EmployeesListSearchViews(generics.ListAPIView):
|
||||||
"""Employee search view"""
|
"""
|
||||||
|
## Employee search view.
|
||||||
|
### *GET*
|
||||||
|
##### Description
|
||||||
|
Return a non-paginated list of employees.
|
||||||
|
Available filters:
|
||||||
|
* search (`str`) - filter by name or last name with mistakes
|
||||||
|
* position_id (`int`) - filter by employees position identifier
|
||||||
|
* public_mark (`str`) - filter by establishment public mark
|
||||||
|
* toque_number (`str`) - filter by establishment toque number
|
||||||
|
* username (`str`) - filter by a username or name
|
||||||
|
(with limitations by the minimum number of characters)
|
||||||
|
|
||||||
|
###### Response
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
"""
|
||||||
pagination_class = None
|
pagination_class = None
|
||||||
queryset = models.Employee.objects.all().with_back_office_related().select_related('photo')
|
|
||||||
filter_class = filters.EmployeeBackSearchFilter
|
filter_class = filters.EmployeeBackSearchFilter
|
||||||
serializer_class = serializers.EmployeeBackSerializers
|
serializer_class = serializers.EmployeeBackSerializers
|
||||||
|
queryset = (
|
||||||
|
models.Employee.objects.with_back_office_related()
|
||||||
|
.select_related('photo')
|
||||||
|
)
|
||||||
permission_classes = get_permission_classes(
|
permission_classes = get_permission_classes(
|
||||||
IsEstablishmentManager,
|
IsEstablishmentManager,
|
||||||
IsEstablishmentAdministrator,
|
IsEstablishmentAdministrator,
|
||||||
|
|
@ -564,7 +653,47 @@ class EmployeesListSearchViews(generics.ListAPIView):
|
||||||
|
|
||||||
|
|
||||||
class EstablishmentEmployeeListView(generics.ListCreateAPIView):
|
class EstablishmentEmployeeListView(generics.ListCreateAPIView):
|
||||||
"""Establishment emplyoees list view."""
|
"""
|
||||||
|
## Establishment employees List/Create view.
|
||||||
|
### *GET*
|
||||||
|
#### Description
|
||||||
|
Returning non-paginated list of employees by establishment identifier.
|
||||||
|
##### Response
|
||||||
|
E.g.:
|
||||||
|
```
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### *POST*
|
||||||
|
#### Description
|
||||||
|
Create a new instance of employee for establishment by establishment identifier.
|
||||||
|
#### Request
|
||||||
|
Required:
|
||||||
|
* name (`str`) - employee name
|
||||||
|
|
||||||
|
Additional:
|
||||||
|
* last_name (`str`) - employee last name
|
||||||
|
* user (`int`) - user identifier
|
||||||
|
* sex (`int`) - enum: `0 (Male), 1 (Female)`
|
||||||
|
* birth_date (`str`) - birth datetime (datetime in a format `ISO-8601`)
|
||||||
|
* email (`str`) - email address
|
||||||
|
* phone (`str`) - phone number in a format `E164`
|
||||||
|
* available_for_events (bool) - flag that responds for availability for events
|
||||||
|
* photo_id (`int`) - photo identifier
|
||||||
|
|
||||||
|
#### Response
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
"""
|
||||||
serializer_class = serializers.EstEmployeeBackSerializer
|
serializer_class = serializers.EstEmployeeBackSerializer
|
||||||
pagination_class = None
|
pagination_class = None
|
||||||
permission_classes = get_permission_classes(
|
permission_classes = get_permission_classes(
|
||||||
|
|
@ -584,7 +713,51 @@ class EstablishmentEmployeeListView(generics.ListCreateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class EmployeeRUDView(generics.RetrieveUpdateDestroyAPIView):
|
class EmployeeRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
"""Employee RUD view."""
|
"""
|
||||||
|
## Employee Retrieve/Update/Destroy view
|
||||||
|
### *GET*
|
||||||
|
#### Description
|
||||||
|
Retrieve a serialized object of employee.
|
||||||
|
##### Response
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### *PUT*/*PATCH*
|
||||||
|
#### Description
|
||||||
|
Completely/Partially update an employee object.
|
||||||
|
##### Request
|
||||||
|
Available fields:
|
||||||
|
* name (`str`) - employee name
|
||||||
|
* last_name (`str`) - employee last name
|
||||||
|
* sex (`enum`) - 0 (Male), 1 (Female)
|
||||||
|
* birth_date (`str`) - datetime in a format `ISO-8601`
|
||||||
|
* email (`str`) - employee email address
|
||||||
|
* phone (`str`) - phone number in E164 format
|
||||||
|
* toque_number (`int`) - employee toque number
|
||||||
|
* available_for_events (`bool`) - flag that responds for availability for events
|
||||||
|
* photo_id (`int`) - image identifier
|
||||||
|
##### Response
|
||||||
|
Return an employee serialized object
|
||||||
|
E.g.:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### *DELETE*
|
||||||
|
#### Description
|
||||||
|
Delete an instance of employee
|
||||||
|
##### Response
|
||||||
|
```
|
||||||
|
No content
|
||||||
|
```
|
||||||
|
"""
|
||||||
serializer_class = serializers.EmployeeBackSerializers
|
serializer_class = serializers.EmployeeBackSerializers
|
||||||
queryset = models.Employee.objects.with_back_office_related()
|
queryset = models.Employee.objects.with_back_office_related()
|
||||||
permission_classes = get_permission_classes(
|
permission_classes = get_permission_classes(
|
||||||
|
|
@ -594,6 +767,16 @@ class EmployeeRUDView(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
|
||||||
|
|
||||||
class RemoveAwardView(generics.DestroyAPIView):
|
class RemoveAwardView(generics.DestroyAPIView):
|
||||||
|
"""
|
||||||
|
## Remove award view.
|
||||||
|
### *DELETE*
|
||||||
|
#### Description
|
||||||
|
Remove an award from an employee by an employee identifier and an award identifier.
|
||||||
|
##### Response
|
||||||
|
```
|
||||||
|
No content
|
||||||
|
```
|
||||||
|
"""
|
||||||
lookup_field = 'pk'
|
lookup_field = 'pk'
|
||||||
serializer_class = serializers.EmployeeBackSerializers
|
serializer_class = serializers.EmployeeBackSerializers
|
||||||
queryset = models.Employee.objects.with_back_office_related()
|
queryset = models.Employee.objects.with_back_office_related()
|
||||||
|
|
@ -856,6 +1039,29 @@ class EstablishmentNoteRUDView(EstablishmentMixinViews,
|
||||||
|
|
||||||
|
|
||||||
class EstablishmentEmployeeCreateView(generics.CreateAPIView):
|
class EstablishmentEmployeeCreateView(generics.CreateAPIView):
|
||||||
|
"""
|
||||||
|
## Create employee position for establishment
|
||||||
|
### *POST*
|
||||||
|
#### Description
|
||||||
|
Creating position for an employee for establishment,
|
||||||
|
by `establishment identifier`, `employee identifier` and
|
||||||
|
`position identifier`.
|
||||||
|
|
||||||
|
##### Request data
|
||||||
|
Available fields:
|
||||||
|
* from_date - datetime (datetime in a format `ISO-8601`), by default `timezone.now()`
|
||||||
|
* to_date - datetime (datetime in a format `ISO-8601`), by default `null`
|
||||||
|
|
||||||
|
##### Response data
|
||||||
|
E.g.:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"id": 47405,
|
||||||
|
"from_date": "2020-02-06T11:01:04.961000Z",
|
||||||
|
"to_date": "2020-02-06T11:01:04.961000Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
"""
|
||||||
serializer_class = serializers.EstablishmentEmployeeCreateSerializer
|
serializer_class = serializers.EstablishmentEmployeeCreateSerializer
|
||||||
queryset = models.EstablishmentEmployee.objects.all()
|
queryset = models.EstablishmentEmployee.objects.all()
|
||||||
permission_classes = get_permission_classes(
|
permission_classes = get_permission_classes(
|
||||||
|
|
@ -865,6 +1071,18 @@ class EstablishmentEmployeeCreateView(generics.CreateAPIView):
|
||||||
|
|
||||||
|
|
||||||
class EstablishmentEmployeeDeleteView(generics.DestroyAPIView):
|
class EstablishmentEmployeeDeleteView(generics.DestroyAPIView):
|
||||||
|
"""
|
||||||
|
## Delete employee position for establishment
|
||||||
|
### *DELETE*
|
||||||
|
#### Description
|
||||||
|
Deleting position for an employee from establishment, by `position identifier`.
|
||||||
|
|
||||||
|
|
||||||
|
##### Response data
|
||||||
|
```
|
||||||
|
No content
|
||||||
|
```
|
||||||
|
"""
|
||||||
queryset = EstablishmentEmployee.objects.all()
|
queryset = EstablishmentEmployee.objects.all()
|
||||||
permission_classes = get_permission_classes(
|
permission_classes = get_permission_classes(
|
||||||
IsEstablishmentManager,
|
IsEstablishmentManager,
|
||||||
|
|
@ -889,7 +1107,25 @@ class EstablishmentPositionListView(generics.ListAPIView):
|
||||||
|
|
||||||
|
|
||||||
class EstablishmentAdminView(generics.ListAPIView):
|
class EstablishmentAdminView(generics.ListAPIView):
|
||||||
"""Establishment admin list view."""
|
"""
|
||||||
|
## List establishment admins
|
||||||
|
### *GET*
|
||||||
|
#### Description
|
||||||
|
Returning paginated list of establishment administrators by establishment slug.
|
||||||
|
##### Response
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"count": 58,
|
||||||
|
"next": 2,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
``` """
|
||||||
serializer_class = serializers.EstablishmentAdminListSerializer
|
serializer_class = serializers.EstablishmentAdminListSerializer
|
||||||
permission_classes = get_permission_classes(
|
permission_classes = get_permission_classes(
|
||||||
IsEstablishmentManager,
|
IsEstablishmentManager,
|
||||||
|
|
|
||||||
18
apps/report/migrations/0002_report_locale.py
Normal file
18
apps/report/migrations/0002_report_locale.py
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.2.7 on 2020-02-06 13:58
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('report', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='report',
|
||||||
|
name='locale',
|
||||||
|
field=models.CharField(max_length=10, null=True, verbose_name='locale'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,22 +1,26 @@
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.utils.functional import cached_property
|
||||||
from django.utils.text import gettext_lazy as _
|
from django.utils.text import gettext_lazy as _
|
||||||
|
|
||||||
from report.tasks import send_report_task
|
from report.tasks import send_report_task
|
||||||
|
from translation.models import SiteInterfaceDictionary
|
||||||
from utils.models import ProjectBaseMixin
|
from utils.models import ProjectBaseMixin
|
||||||
|
|
||||||
|
|
||||||
class ReportManager(models.Manager):
|
class ReportManager(models.Manager):
|
||||||
"""Manager for model Report."""
|
"""Manager for model Report."""
|
||||||
|
|
||||||
def make(self, source: int, category, url: str, description: str):
|
def make(self, source: int, category, url: str, description: str, locale: str):
|
||||||
"""Make object."""
|
"""Make object."""
|
||||||
obj = self.create(
|
obj = self.create(
|
||||||
source=source,
|
source=source,
|
||||||
category=category,
|
category=category,
|
||||||
url=url,
|
url=url,
|
||||||
description=description
|
description=description,
|
||||||
|
locale=locale,
|
||||||
)
|
)
|
||||||
if settings.USE_CELERY:
|
if settings.USE_CELERY:
|
||||||
send_report_task.delay(obj.id)
|
send_report_task.delay(obj.id)
|
||||||
|
|
@ -60,6 +64,8 @@ class Report(ProjectBaseMixin):
|
||||||
verbose_name=_('category'))
|
verbose_name=_('category'))
|
||||||
url = models.URLField(verbose_name=_('URL'))
|
url = models.URLField(verbose_name=_('URL'))
|
||||||
description = models.TextField(verbose_name=_('description'))
|
description = models.TextField(verbose_name=_('description'))
|
||||||
|
locale = models.CharField(max_length=10, null=True,
|
||||||
|
verbose_name=_('locale'))
|
||||||
|
|
||||||
objects = ReportManager.from_queryset(ReportQuerySet)()
|
objects = ReportManager.from_queryset(ReportQuerySet)()
|
||||||
|
|
||||||
|
|
@ -70,19 +76,46 @@ class Report(ProjectBaseMixin):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Implement `str` dunder method."""
|
"""Implement `str` dunder method."""
|
||||||
return f'{self.id}: {self.get_category_display()} ({self.url})'
|
return f'{self.id}: {self.get_category_display()} ({self.url}, {self.locale})'
|
||||||
|
|
||||||
def get_body_email_message(self):
|
@cached_property
|
||||||
|
def support_email_note(self):
|
||||||
|
keyword = 'support.email.note'
|
||||||
|
default_note = (
|
||||||
|
'You received this message because you are an '
|
||||||
|
'administrator with privileges to manage this request.'
|
||||||
|
)
|
||||||
|
|
||||||
|
note_qs = SiteInterfaceDictionary.objects.filter(keywords=keyword)
|
||||||
|
if note_qs.exists():
|
||||||
|
return note_qs.first().text.get(self.locale, default_note)
|
||||||
|
return default_note
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def report_message(self):
|
||||||
|
return render_to_string(
|
||||||
|
template_name=settings.REPORT_TEMPLATE,
|
||||||
|
context={
|
||||||
|
'source_value': self.get_source_display(),
|
||||||
|
'source_page_url': self.url,
|
||||||
|
'source_screen_language': self.locale,
|
||||||
|
'request_category': self.get_category_display(),
|
||||||
|
'request_description': self.description,
|
||||||
|
'support_email_note': self.support_email_note,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def base_template(self):
|
||||||
"""Prepare the body of the email message"""
|
"""Prepare the body of the email message"""
|
||||||
return {
|
return {
|
||||||
'subject': self.get_category_display(),
|
'subject': _('[TECH_REQUEST] A new technical request has been created.'),
|
||||||
'message': str(self.description),
|
'message': self.report_message,
|
||||||
'html_message': self.description,
|
'html_message': self.report_message,
|
||||||
'from_email': settings.EMAIL_HOST_USER,
|
'from_email': settings.EMAIL_HOST_USER,
|
||||||
'recipient_list': [settings.EMAIL_TECHNICAL_SUPPORT, ],
|
'recipient_list': [settings.EMAIL_TECHNICAL_SUPPORT, ],
|
||||||
}
|
}
|
||||||
|
|
||||||
def send_email(self):
|
def send_email(self):
|
||||||
"""Send an email reset user password"""
|
"""Send an email reset user password"""
|
||||||
send_mail(**self.get_body_email_message())
|
send_mail(**self.base_template)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
"""DRF-serializers for application report."""
|
"""DRF-serializers for application report."""
|
||||||
|
from django.utils.functional import cached_property
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from report.models import Report
|
from report.models import Report
|
||||||
|
|
@ -18,15 +19,25 @@ class ReportBaseSerializer(serializers.ModelSerializer):
|
||||||
'category_display',
|
'category_display',
|
||||||
'url',
|
'url',
|
||||||
'description',
|
'description',
|
||||||
|
'locale',
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'source': {'required': False},
|
'source': {'required': False},
|
||||||
'category': {'write_only': True}
|
'category': {'write_only': True},
|
||||||
|
'locale': {'write_only': True}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@cached_property
|
||||||
|
def locale(self) -> str:
|
||||||
|
"""Return locale from request."""
|
||||||
|
request = self.context.get('request')
|
||||||
|
if hasattr(request, 'locale'):
|
||||||
|
return request.locale
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
"""An overridden validate method."""
|
"""An overridden validate method."""
|
||||||
attrs['source'] = self.context.get('view').get_source()
|
attrs['source'] = self.context.get('view').get_source()
|
||||||
|
attrs['locale'] = self.locale
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class ReportListCreateView(ReportBaseView, ListCreateAPIView):
|
||||||
* category: integer (0 - Bug, 1 - Suggestion improvement)
|
* category: integer (0 - Bug, 1 - Suggestion improvement)
|
||||||
* url: char (URL)
|
* url: char (URL)
|
||||||
* description: text (problem description)
|
* description: text (problem description)
|
||||||
I.e.:
|
E.g.:
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"category": 1,
|
"category": 1,
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ class ReportRetrieveView(ReportBaseView, generics.RetrieveAPIView):
|
||||||
## View for retrieving serialized instance.
|
## View for retrieving serialized instance.
|
||||||
### Response
|
### Response
|
||||||
Return serialized object.
|
Return serialized object.
|
||||||
I.e.:
|
E.g.:
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"count": 7,
|
"count": 7,
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ class ReportListCreateView(ReportBaseView, ListCreateAPIView):
|
||||||
* category: integer (0 - Bug, 1 - Suggestion improvement)
|
* category: integer (0 - Bug, 1 - Suggestion improvement)
|
||||||
* url: char (URL)
|
* url: char (URL)
|
||||||
* description: text (problem description)
|
* description: text (problem description)
|
||||||
I.e.:
|
E.g.:
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"category": 1,
|
"category": 1,
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ from django.contrib.postgres.fields.jsonb import KeyTextTransform
|
||||||
from django.core.validators import FileExtensionValidator
|
from django.core.validators import FileExtensionValidator
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from django.utils.functional import cached_property
|
||||||
from django.utils.html import mark_safe
|
from django.utils.html import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _, get_language
|
from django.utils.translation import ugettext_lazy as _, get_language
|
||||||
from easy_thumbnails.fields import ThumbnailerImageField
|
from easy_thumbnails.fields import ThumbnailerImageField
|
||||||
|
|
@ -516,13 +517,13 @@ def default_menu_bool_array():
|
||||||
|
|
||||||
class PhoneModelMixin:
|
class PhoneModelMixin:
|
||||||
"""Mixin for PhoneNumberField."""
|
"""Mixin for PhoneNumberField."""
|
||||||
@property
|
@cached_property
|
||||||
def country_calling_code(self):
|
def country_calling_code(self):
|
||||||
"""Return phone code from PhonеNumberField."""
|
"""Return phone code from PhonеNumberField."""
|
||||||
if hasattr(self, 'phone') and self.phone:
|
if hasattr(self, 'phone') and self.phone:
|
||||||
return f'+{self.phone.country_code}'
|
return f'+{self.phone.country_code}'
|
||||||
|
|
||||||
@property
|
@cached_property
|
||||||
def national_calling_number(self):
|
def national_calling_number(self):
|
||||||
"""Return phone national number from from PhonеNumberField."""
|
"""Return phone national number from from PhonеNumberField."""
|
||||||
if hasattr(self, 'phone') and (self.phone and hasattr(self.phone, 'national_number')):
|
if hasattr(self, 'phone') and (self.phone and hasattr(self.phone, 'national_number')):
|
||||||
|
|
|
||||||
|
|
@ -462,6 +462,7 @@ CONFIRM_EMAIL_TEMPLATE = 'authorization/confirm_email.html'
|
||||||
NEWS_EMAIL_TEMPLATE = 'news/news_email.html'
|
NEWS_EMAIL_TEMPLATE = 'news/news_email.html'
|
||||||
NOTIFICATION_PASSWORD_TEMPLATE = 'account/password_change_email.html'
|
NOTIFICATION_PASSWORD_TEMPLATE = 'account/password_change_email.html'
|
||||||
NOTIFICATION_SUBSCRIBE_TEMPLATE = 'notification/update_email.html'
|
NOTIFICATION_SUBSCRIBE_TEMPLATE = 'notification/update_email.html'
|
||||||
|
REPORT_TEMPLATE = 'report/tech_support_template.html'
|
||||||
|
|
||||||
|
|
||||||
# COOKIES
|
# COOKIES
|
||||||
|
|
@ -568,4 +569,4 @@ COUNTRY_CALLING_CODES = {
|
||||||
CALLING_CODES_ANTILLES_GUYANE_WEST_INDIES = [590, 594, 1758, 596]
|
CALLING_CODES_ANTILLES_GUYANE_WEST_INDIES = [590, 594, 1758, 596]
|
||||||
DEFAULT_CALLING_CODE_ANTILLES_GUYANE_WEST_INDIES = 590
|
DEFAULT_CALLING_CODE_ANTILLES_GUYANE_WEST_INDIES = 590
|
||||||
|
|
||||||
EMAIL_TECHNICAL_SUPPORT = 'it-report@gaultmillau.com'
|
EMAIL_TECHNICAL_SUPPORT = 'tech_requests@gaultmillau.com'
|
||||||
|
|
|
||||||
49
project/templates/report/tech_support_template.html
Normal file
49
project/templates/report/tech_support_template.html
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user