added report app

This commit is contained in:
Anatoly 2020-02-05 16:46:22 +03:00
parent ab3666b03d
commit 8eb07205e1
25 changed files with 394 additions and 5 deletions

0
apps/report/__init__.py Normal file
View File

8
apps/report/admin.py Normal file
View File

@ -0,0 +1,8 @@
from django.contrib import admin
from report.models import Report
@admin.register(Report)
class ReportAdmin(admin.ModelAdmin):
"""Report admin model."""

8
apps/report/apps.py Normal file
View File

@ -0,0 +1,8 @@
from django.apps import AppConfig
from django.utils.text import gettext_lazy as _
class ReportConfig(AppConfig):
name = 'report'
verbose_name = _('Report')

26
apps/report/filters.py Normal file
View File

@ -0,0 +1,26 @@
"""Filters for application report."""
from django_filters import rest_framework as filters
from report.models import Report
class ReportFilterSet(filters.FilterSet):
"""Report filter set."""
source = filters.ChoiceFilter(
choices=Report.SOURCE_CHOICES,
help_text='Filter allow filtering QuerySet by a field - source.'
'Choices - 0 (Back office), 1 (Web), 2 (Mobile)'
)
category = filters.ChoiceFilter(
choices=Report.CATEGORY_CHOICES,
help_text='Filter allow filtering QuerySet by a field - category.'
'Choices - 0 (Bug), 1 (Suggestion/improvement)'
)
class Meta:
"""Meta class."""
model = Report
fields = (
'source',
'category',
)

View File

@ -0,0 +1,31 @@
# Generated by Django 2.2.7 on 2020-02-05 12:16
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Report',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(default=django.utils.timezone.now, editable=False, verbose_name='Date created')),
('modified', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
('source', models.PositiveSmallIntegerField(choices=[(0, 'Back office'), (1, 'Web'), (2, 'Mobile')], verbose_name='source')),
('category', models.PositiveSmallIntegerField(choices=[(0, 'Bug'), (1, 'Suggestion/improvement')], verbose_name='category')),
('url', models.URLField(verbose_name='URL')),
('description', models.TextField(verbose_name='description')),
],
options={
'verbose_name': 'Report',
'verbose_name_plural': 'Reports',
},
),
]

View File

84
apps/report/models.py Normal file
View File

@ -0,0 +1,84 @@
from django.conf import settings
from django.core.mail import send_mail
from django.db import models
from django.utils.text import gettext_lazy as _
from report.tasks import send_report_task
from utils.models import ProjectBaseMixin
class ReportManager(models.Manager):
"""Manager for model Report."""
def make(self, source: int, category, url: str, description: str):
"""Make object."""
obj = self.create(
source=source,
category=category,
url=url,
description=description
)
if settings.USE_CELERY:
send_report_task.delay(obj.id)
else:
send_report_task(obj.id)
return obj
class ReportQuerySet(models.QuerySet):
"""QuerySet for model Report."""
def by_source(self, source: int):
"""Return QuerySet filtered by a source."""
return self.filter(source=source)
class Report(ProjectBaseMixin):
"""Report model."""
BACK_OFFICE = 0
WEB = 1
MOBILE = 2
SOURCE_CHOICES = (
(BACK_OFFICE, _('Back office')),
(WEB, _('Web')),
(MOBILE, _('Mobile')),
)
BUG = 0
SUGGESTION_IMPROVEMENT = 1
CATEGORY_CHOICES = (
(BUG, _('Bug')),
(SUGGESTION_IMPROVEMENT, _('Suggestion/improvement')),
)
source = models.PositiveSmallIntegerField(choices=SOURCE_CHOICES,
verbose_name=_('source'))
category = models.PositiveSmallIntegerField(choices=CATEGORY_CHOICES,
verbose_name=_('category'))
url = models.URLField(verbose_name=_('URL'))
description = models.TextField(verbose_name=_('description'))
objects = ReportManager.from_queryset(ReportQuerySet)()
class Meta:
"""Meta class."""
verbose_name = _('Report')
verbose_name_plural = _('Reports')
def get_body_email_message(self):
"""Prepare the body of the email message"""
return {
'subject': self.get_category_display(),
'message': str(self.description),
'html_message': self.description,
'from_email': settings.EMAIL_HOST_USER,
'recipient_list': [settings.EMAIL_TECHNICAL_SUPPORT, ],
}
def send_email(self):
"""Send an email reset user password"""
send_mail(**self.get_body_email_message())

View File

@ -0,0 +1,34 @@
"""DRF-serializers for application report."""
from rest_framework import serializers
from report.models import Report
class ReportBaseSerializer(serializers.ModelSerializer):
"""Serializer for model Report."""
category_display = serializers.CharField(source='get_category_display', read_only=True)
class Meta:
"""Meta class."""
model = Report
fields = [
'id',
'category',
'category_display',
'url',
'description',
]
extra_kwargs = {
'source': {'required': False},
'category': {'write_only': True}
}
def validate(self, attrs):
"""An overridden validate method."""
attrs['source'] = self.context.get('view').get_source()
return attrs
def create(self, validated_data):
"""An overridden create method."""
return self.Meta.model.objects.make(**validated_data)

18
apps/report/tasks.py Normal file
View File

@ -0,0 +1,18 @@
"""Report app tasks."""
import logging
from celery import shared_task
logger = logging.getLogger(__name__)
@shared_task
def send_report_task(report_id: int):
from report.models import Report
report_qs = Report.objects.filter(id=report_id)
if report_qs.exists():
report = report_qs.first()
report.send_email()
else:
logger.error(f'Error sending report {report_id}')

1
apps/report/tests.py Normal file
View File

@ -0,0 +1 @@
# Create your tests here.

View File

10
apps/report/urls/back.py Normal file
View File

@ -0,0 +1,10 @@
"""Back office URL patterns for application report."""
from django.urls import path
from report.views import back as views
app_name = 'report'
urlpatterns = [
path('', views.ReportListCreateView.as_view(), name='report-list-create'),
]

View File

@ -0,0 +1,9 @@
"""Common URL patterns for application report."""
from django.urls import path
from report.views import common as views
app_name = 'report'
urlpatterns = [
path('<int:pk>/', views.ReportRetrieveView.as_view(), name='report-retrieve')
]

View File

@ -0,0 +1,5 @@
"""Mobile URL patterns for application report."""
app_name = 'report'
urlpatterns = []

11
apps/report/urls/web.py Normal file
View File

@ -0,0 +1,11 @@
"""Web URL patterns for application report."""
from django.urls import path
from report.urls.common import urlpatterns as common_urlpatterns
from report.views import web as views
app_name = 'report'
urlpatterns = common_urlpatterns + [
path('', views.ReportListCreateView.as_view(), name='report-list-create'),
]

View File

39
apps/report/views/back.py Normal file
View File

@ -0,0 +1,39 @@
"""Views for application report."""
from rest_framework.generics import ListCreateAPIView
from report.models import Report
from report.views.common import ReportBaseView
class ReportListCreateView(ReportBaseView, ListCreateAPIView):
"""
## View for getting list of reports or create a new one.
### POST-request data
Request data attributes:
* category: integer (0 - Bug, 1 - Suggestion improvement)
* url: char (URL)
* description: text (problem description)
I.e.:
```
{
"category": 1,
"url": "http://google.com",
"description": "Description"
}
```
### Response
*GET*
Return paginated list of reports.
*POST*
Creates a new report, and returns a serialized object.
### Description
Method that allows getting list of reports or create a new one and return serialized object.
"""
@staticmethod
def get_source():
"""Return a source for view."""
return Report.BACK_OFFICE

View File

@ -0,0 +1,58 @@
"""Common views for application report."""
from rest_framework import generics
from report.filters import ReportFilterSet
from report.models import Report
from report.serializers import ReportBaseSerializer
from utils.methods import get_permission_classes
from utils.permissions import (
IsEstablishmentManager, IsContentPageManager, IsReviewManager,
IsModerator, IsRestaurantInspector, IsArtisanInspector,
IsWineryWineInspector, IsDistilleryLiquorInspector, IsProducerFoodInspector,
IsEstablishmentAdministrator
)
class ReportBaseView(generics.GenericAPIView):
"""
## Report base view.
"""
queryset = Report.objects.all()
serializer_class = ReportBaseSerializer
filter_class = ReportFilterSet
permission_classes = get_permission_classes(
IsEstablishmentManager, IsContentPageManager, IsReviewManager,
IsModerator, IsRestaurantInspector, IsArtisanInspector,
IsWineryWineInspector, IsDistilleryLiquorInspector, IsProducerFoodInspector,
IsEstablishmentAdministrator
)
@staticmethod
def get_source():
"""Return a source for view."""
return NotImplementedError('You must implement "get_source" method')
class ReportRetrieveView(ReportBaseView, generics.RetrieveAPIView):
"""
## View for retrieving serialized instance.
### Response
Return serialized object.
I.e.:
```
{
"count": 7,
"next": null,
"previous": null,
"results": [
{
"id": 1,
...
}
]
}
```
### Description
Method that allows retrieving serialized report object.
"""

39
apps/report/views/web.py Normal file
View File

@ -0,0 +1,39 @@
"""Views for application report."""
from rest_framework.generics import ListCreateAPIView
from report.models import Report
from report.views.common import ReportBaseView
class ReportListCreateView(ReportBaseView, ListCreateAPIView):
"""
## View for getting list of reports or create a new one.
### POST-request data
Request data attributes:
* category: integer (0 - Bug, 1 - Suggestion improvement)
* url: char (URL)
* description: text (problem description)
I.e.:
```
{
"category": 1,
"url": "http://google.com",
"description": "Description"
}
```
### Response
*GET*
Return paginated list of reports.
*POST*
Creates a new report, and returns a serialized object.
### Description
Method that allows getting list of reports or create a new one and return serialized object.
"""
@staticmethod
def get_source():
"""Return a source for view."""
return Report.WEB

View File

@ -1,11 +1,13 @@
"""Translation app models."""
from django.apps import apps
from django.contrib.postgres.fields import JSONField
from django.contrib.postgres.indexes import GinIndex
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.apps import apps
from utils.models import ProjectBaseMixin, LocaleManagerMixin
class LanguageQuerySet(models.QuerySet):
"""QuerySet for model Language"""
@ -55,7 +57,7 @@ class SiteInterfaceDictionaryManager(LocaleManagerMixin):
Tag = apps.get_model('tag', 'Tag')
"""Creates or updates translation for EXISTING in DB Tag"""
if not tag.pk or not isinstance(tag, Tag):
raise NotImplementedError
raise NotImplementedError()
if tag.translation:
tag.translation.text = translations
tag.translation.page = 'tag'
@ -74,7 +76,7 @@ class SiteInterfaceDictionaryManager(LocaleManagerMixin):
"""Creates or updates translation for EXISTING in DB TagCategory"""
TagCategory = apps.get_model('tag', 'TagCategory')
if not tag_category.pk or not isinstance(tag_category, TagCategory):
raise NotImplementedError
raise NotImplementedError()
if tag_category.translation:
tag_category.translation.text = translations
tag_category.translation.page = 'tag'

View File

@ -143,7 +143,7 @@ class OAuthProjectMixin:
def get_source(self):
"""Method to get of platform"""
return NotImplementedError
return NotImplementedError()
class BaseAttributes(ProjectBaseMixin):

View File

@ -76,6 +76,7 @@ PROJECT_APPS = [
'favorites.apps.FavoritesConfig',
'rating.apps.RatingConfig',
'tag.apps.TagConfig',
'report.apps.ReportConfig',
]
EXTERNAL_APPS = [
@ -566,3 +567,5 @@ COUNTRY_CALLING_CODES = {
CALLING_CODES_ANTILLES_GUYANE_WEST_INDIES = [590, 594, 1758, 596]
DEFAULT_CALLING_CODE_ANTILLES_GUYANE_WEST_INDIES = 590
EMAIL_TECHNICAL_SUPPORT = 'it-report@gaultmillau.com'

View File

@ -123,3 +123,5 @@ EMAIL_PORT = 587
# ADD TRANSFER TO INSTALLED APPS
INSTALLED_APPS.append('transfer.apps.TransferConfig')
EMAIL_TECHNICAL_SUPPORT = 'a.feteleu@spider.ru'

View File

@ -18,4 +18,5 @@ urlpatterns = [
namespace='advertisement')),
path('main/', include('main.urls.back')),
path('partner/', include('partner.urls.back')),
path('report/', include('report.urls.back')),
]

View File

@ -36,5 +36,5 @@ urlpatterns = [
path('favorites/', include('favorites.urls')),
path('timetables/', include('timetable.urls.web')),
path('products/', include('product.urls.web')),
path('report/', include('report.urls.web')),
]