Merge branch 'develop' into feature/establishment

This commit is contained in:
evgeniy-st 2019-08-26 18:26:05 +03:00
commit 21985eb8bc
31 changed files with 290 additions and 28 deletions

View File

View File

@ -0,0 +1,9 @@
"""Admin models for app advertisement"""
from django.contrib import admin
from advertisement import models
@admin.register(models.Advertisement)
class AdvertisementModelAdmin(admin.ModelAdmin):
"""Admin model for model Advertisement"""

View File

@ -0,0 +1,7 @@
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class AdvertisementConfig(AppConfig):
name = 'advertisement'
verbose_name = _('advertisement')

View File

@ -0,0 +1,34 @@
# Generated by Django 2.2.4 on 2019-08-26 12:24
from django.db import migrations, models
import django.utils.timezone
import easy_thumbnails.fields
import utils.methods
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Advertisement',
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')),
('image', easy_thumbnails.fields.ThumbnailerImageField(blank=True, default=None, null=True, upload_to=utils.methods.image_path, verbose_name='Image')),
('url', models.URLField(verbose_name='Ad URL')),
('width', models.PositiveIntegerField(verbose_name='Block width')),
('height', models.PositiveIntegerField(verbose_name='Block height')),
('block_level', models.PositiveSmallIntegerField(choices=[(1, 'Ad block level 1'), (2, 'Ad block level 2'), (3, 'Ad block level 3')], verbose_name='Block level')),
],
options={
'verbose_name': 'Advertisement',
'verbose_name_plural': 'Advertisement',
},
),
]

View File

@ -0,0 +1,32 @@
"""Advertisement app models."""
from django.db import models
from django.utils.translation import gettext_lazy as _
from utils.models import ProjectBaseMixin, ImageMixin
class Advertisement(ImageMixin, ProjectBaseMixin):
"""Advertisement model."""
LEVEL_1 = 1
LEVEL_2 = 2
LEVEL_3 = 3
BLOCK_LEVEL_CHOICES = (
(LEVEL_1, _('Ad block level 1')),
(LEVEL_2, _('Ad block level 2')),
(LEVEL_3, _('Ad block level 3'))
)
url = models.URLField(verbose_name=_('Ad URL'))
width = models.PositiveIntegerField(verbose_name=_('Block width'))
height = models.PositiveIntegerField(verbose_name=_('Block height'))
block_level = models.PositiveSmallIntegerField(choices=BLOCK_LEVEL_CHOICES,
verbose_name=_('Block level'))
class Meta:
verbose_name = _('Advertisement')
verbose_name_plural = _('Advertisement')
def __str__(self):
return str(self.url)

View File

@ -0,0 +1,19 @@
"""Serializers for app advertisements"""
from rest_framework import serializers
from advertisement import models
class AdvertisementSerializer(serializers.ModelSerializer):
"""Serializer for model Advertisement."""
class Meta:
model = models.Advertisement
fields = (
'id',
'url',
'image',
'width',
'height',
'block_level',
)

View File

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

View File

View File

@ -0,0 +1,10 @@
"""Advertisement common urlpaths."""
from django.urls import path
from advertisement.views import web as views
app_name = 'advertisement'
urlpatterns = [
path('list/', views.AdvertisementListView.as_view(), name='advertisements_list')
]

View File

View File

@ -0,0 +1,15 @@
"""Views for app advertisement"""
from rest_framework import generics
from rest_framework import permissions
from advertisement import models
from advertisement.serializers import web as serializers
class AdvertisementListView(generics.ListAPIView):
"""List view for model Advertisement"""
pagination_class = None
model = models.Advertisement
permission_classes = (permissions.AllowAny, )
queryset = models.Advertisement.objects.all()
serializer_class = serializers.AdvertisementSerializer

View File

@ -72,7 +72,6 @@ class Collection(ProjectBaseMixin, CollectionNameMixin,
return f'{self.name}' return f'{self.name}'
class CollectionItemQuerySet(models.QuerySet): class CollectionItemQuerySet(models.QuerySet):
"""QuerySet for model CollectionItem.""" """QuerySet for model CollectionItem."""

View File

@ -27,6 +27,7 @@ class GuideViewMixin(generics.GenericAPIView):
# Views # Views
class CollectionListView(CollectionViewMixin, generics.ListAPIView): class CollectionListView(CollectionViewMixin, generics.ListAPIView):
"""List Collection view""" """List Collection view"""
pagination_class = None
permission_classes = (permissions.AllowAny,) permission_classes = (permissions.AllowAny,)
serializer_class = serializers.CollectionSerializer serializer_class = serializers.CollectionSerializer

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.4 on 2019-08-26 08:42
from django.db import migrations
import easy_thumbnails.fields
import utils.methods
class Migration(migrations.Migration):
dependencies = [
('location', '0005_auto_20190822_1144'),
]
operations = [
migrations.AddField(
model_name='country',
name='image',
field=easy_thumbnails.fields.ThumbnailerImageField(blank=True, default=None, null=True, upload_to=utils.methods.image_path, verbose_name='Image'),
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 2.2.4 on 2019-08-26 13:42
from django.db import migrations, models
import utils.methods
import utils.validators
class Migration(migrations.Migration):
dependencies = [
('location', '0006_country_image'),
]
operations = [
migrations.RemoveField(
model_name='country',
name='image',
),
migrations.AddField(
model_name='country',
name='svg_image',
field=models.FileField(blank=True, default=None, null=True, upload_to=utils.methods.svg_image_path, validators=[utils.validators.svg_image_validator], verbose_name='SVG image'),
),
]

View File

@ -1,15 +1,16 @@
"""Location app models.""" """Location app models."""
from django.contrib.postgres.fields import JSONField
from django.contrib.gis.db import models from django.contrib.gis.db import models
from django.contrib.postgres.fields import JSONField
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from utils.models import ProjectBaseMixin, LocaleManagerMixin
from utils.models import ProjectBaseMixin, LocaleManagerMixin, SVGImageMixin
class CountryManager(LocaleManagerMixin): class CountryManager(LocaleManagerMixin):
"""Extended manager for Country model.""" """Extended manager for Country model."""
class Country(ProjectBaseMixin): class Country(SVGImageMixin, ProjectBaseMixin):
"""Country model.""" """Country model."""
name = JSONField(null=True, blank=True, default=None, name = JSONField(null=True, blank=True, default=None,

View File

@ -1,6 +1,7 @@
from rest_framework import serializers
from location import models
from django.contrib.gis.geos import Point from django.contrib.gis.geos import Point
from rest_framework import serializers
from location import models
class CountrySerializer(serializers.ModelSerializer): class CountrySerializer(serializers.ModelSerializer):
@ -13,6 +14,7 @@ class CountrySerializer(serializers.ModelSerializer):
fields = [ fields = [
'id', 'id',
'code', 'code',
'svg_image',
'name_trans', 'name_trans',
] ]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2019-08-26 14:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0006_auto_20190822_1118'),
]
operations = [
migrations.AddField(
model_name='sitefeature',
name='main',
field=models.BooleanField(default=False, verbose_name='Main'),
),
]

View File

@ -133,6 +133,10 @@ class SiteSettings(ProjectBaseMixin):
return self.feature_set.filter(sitefeature__site_settings=self, return self.feature_set.filter(sitefeature__site_settings=self,
sitefeature__published=True) sitefeature__published=True)
@property
def published_sitefeatures(self):
return self.sitefeature_set.filter(published=True)
@property @property
def site_url(self): def site_url(self):
return methods.site_url(schema=settings.SCHEMA_URI, return methods.site_url(schema=settings.SCHEMA_URI,
@ -168,6 +172,7 @@ class SiteFeature(ProjectBaseMixin):
site_settings = models.ForeignKey(SiteSettings, on_delete=models.CASCADE) site_settings = models.ForeignKey(SiteSettings, on_delete=models.CASCADE)
feature = models.ForeignKey(Feature, on_delete=models.PROTECT) feature = models.ForeignKey(Feature, on_delete=models.PROTECT)
published = models.BooleanField(default=False, verbose_name=_('Published')) published = models.BooleanField(default=False, verbose_name=_('Published'))
main = models.BooleanField(default=False, verbose_name=_('Main'))
objects = SiteFeatureQuerySet.as_manager() objects = SiteFeatureQuerySet.as_manager()

View File

@ -16,11 +16,23 @@ class FeatureSerializer(serializers.ModelSerializer):
) )
class SiteFeatureSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source='feature.id')
slug = serializers.CharField(source='feature.slug')
class Meta:
"""Meta class."""
model = models.SiteFeature
fields = ('main',
'id',
'slug')
class SiteSettingsSerializer(serializers.ModelSerializer): class SiteSettingsSerializer(serializers.ModelSerializer):
"""Site settings serializer.""" """Site settings serializer."""
published_features = FeatureSerializer(many=True, allow_null=None) published_features = SiteFeatureSerializer(many=True, allow_null=True)
#todo: remove this #todo: remove this
country_code = serializers.CharField(source='subdomain', read_only=True) country_code = serializers.CharField(source='subdomain', read_only=True)
@ -42,16 +54,16 @@ class SiteSettingsSerializer(serializers.ModelSerializer):
) )
class SiteFeatureSerializer(serializers.ModelSerializer): # class SiteFeatureSerializer(serializers.ModelSerializer):
"""Site feature serializer.""" # """Site feature serializer."""
#
class Meta: # class Meta:
"""Meta class.""" # """Meta class."""
#
model = models.SiteFeature # model = models.SiteFeature
fields = ( # fields = (
'id', # 'id',
'published', # 'published',
'site_settings', # 'site_settings',
'feature', # 'feature',
) # )

View File

@ -50,6 +50,7 @@ class NewsCreateUpdateSerializer(NewsSerializer):
title = serializers.JSONField() title = serializers.JSONField()
subtitle = serializers.JSONField() subtitle = serializers.JSONField()
description = serializers.JSONField() description = serializers.JSONField()
image = serializers.ImageField(required=True)
news_type = serializers.PrimaryKeyRelatedField( news_type = serializers.PrimaryKeyRelatedField(
queryset=models.NewsType.objects.all(), write_only=True) queryset=models.NewsType.objects.all(), write_only=True)
address = serializers.PrimaryKeyRelatedField( address = serializers.PrimaryKeyRelatedField(
@ -69,5 +70,8 @@ class NewsCreateUpdateSerializer(NewsSerializer):
'start', 'start',
'end', 'end',
'playlist', 'playlist',
'address' 'address',
] 'image',
'is_publish',
'country'
]

View File

@ -7,9 +7,9 @@ app_name = 'news'
urlpatterns = [ urlpatterns = [
path('', common.NewsList.as_view(), name='news_list'), path('', common.NewsList.as_view(), name='news_list'),
# path('create/', common.NewsCreate.as_view(), name='news_create'), path('create/', common.NewsCreate.as_view(), name='news_create'),
# path('<int:pk>/', common.NewsDetail.as_view(), name='news_detail'), path('<int:pk>/', common.NewsDetail.as_view(), name='news_detail'),
# path('<int:pk>/update/', common.NewsUpdate.as_view(), name='news_update'), path('<int:pk>/update/', common.NewsUpdate.as_view(), name='news_update'),
# path('<int:pk>/delete/', common.NewsDelete.as_view(), name='news_delete'), path('<int:pk>/delete/', common.NewsDelete.as_view(), name='news_delete'),
path('type/', common.NewsTypeList.as_view(), name='news_type'), path('type/', common.NewsTypeList.as_view(), name='news_type'),
] ]

View File

@ -14,5 +14,6 @@ class PartnerViewMixin(generics.GenericAPIView):
# Views # Views
class PartnerListView(PartnerViewMixin, generics.ListAPIView): class PartnerListView(PartnerViewMixin, generics.ListAPIView):
"""List Partner view""" """List Partner view"""
pagination_class = None
permission_classes = (permissions.AllowAny, ) permission_classes = (permissions.AllowAny, )
serializer_class = serializers.PartnerSerializer serializer_class = serializers.PartnerSerializer

View File

@ -1,6 +1,7 @@
"""Utils app method.""" """Utils app method."""
import random import random
import re import re
from django.conf import settings from django.conf import settings
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.utils.timezone import datetime from django.utils.timezone import datetime
@ -40,3 +41,12 @@ def image_path(instance, filename):
instance._meta.model_name, instance._meta.model_name,
datetime.now().strftime(settings.REST_DATE_FORMAT), datetime.now().strftime(settings.REST_DATE_FORMAT),
filename) filename)
def svg_image_path(instance, filename):
"""Determine SVG path method."""
filename = '%s.svg' % generate_code()
return 'image/svg/%s/%s/%s' % (
instance._meta.model_name,
datetime.now().strftime(settings.REST_DATE_FORMAT),
filename)

View File

@ -9,7 +9,8 @@ from django.utils.html import mark_safe
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from easy_thumbnails.fields import ThumbnailerImageField from easy_thumbnails.fields import ThumbnailerImageField
from utils.methods import image_path from utils.methods import image_path, svg_image_path
from utils.validators import svg_image_validator
class ProjectBaseMixin(models.Model): class ProjectBaseMixin(models.Model):
@ -86,6 +87,18 @@ class ImageMixin(models.Model):
image_tag.allow_tags = True image_tag.allow_tags = True
class SVGImageMixin(models.Model):
"""SVG image model."""
svg_image = models.FileField(upload_to=svg_image_path,
blank=True, null=True, default=None,
validators=[svg_image_validator, ],
verbose_name=_('SVG image'))
class Meta:
abstract = True
class PlatformMixin(models.Model): class PlatformMixin(models.Model):
"""Platforms""" """Platforms"""

22
apps/utils/validators.py Normal file
View File

@ -0,0 +1,22 @@
"""DB field validators"""
import xml.etree.cElementTree as et
from django.core.exceptions import ValidationError
def svg_image_validator(file: object) -> object:
"""Validate SVG file"""
tag = None
try:
for event, el in et.iterparse(file, ('start',)):
tag = el.tag
break
assert tag == '{http://www.w3.org/2000/svg}svg'
except:
raise ValidationError(
message='Invalid SVG image file',
code='invalid_svg_image',
params={'value': file},
)
else:
return file

View File

@ -52,6 +52,7 @@ CONTRIB_APPS = [
PROJECT_APPS = [ PROJECT_APPS = [
'advertisement.apps.AdvertisementConfig',
'account.apps.AccountConfig', 'account.apps.AccountConfig',
'authorization.apps.AuthorizationConfig', 'authorization.apps.AuthorizationConfig',
'collection.apps.CollectionConfig', 'collection.apps.CollectionConfig',

View File

@ -76,4 +76,5 @@ urlpatterns = urlpatterns + \
static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG: if settings.DEBUG:
urlpatterns.extend(urlpatterns_doc) urlpatterns.extend(urlpatterns_doc)

View File

@ -22,4 +22,5 @@ urlpatterns = [
path('news/', include('news.urls.web')), path('news/', include('news.urls.web')),
path('collection/', include('collection.urls.web')), path('collection/', include('collection.urls.web')),
path('partner/', include('partner.urls.web')), path('partner/', include('partner.urls.web')),
path('advertisement/', include('advertisement.urls.web'))
] ]