Feature/mobile features

This commit is contained in:
Semyon Yekhmenin 2019-10-17 13:29:29 +00:00 committed by e.stoyushko
parent 8a363498f5
commit 1ff571b767
16 changed files with 185 additions and 90 deletions

View File

@ -1,7 +1,6 @@
"""Location app web urlconf.""" """Location app web urlconf."""
from location.urls.common import urlpatterns as common_urlpatterns from location.urls.common import urlpatterns as common_urlpatterns
urlpatterns = [] urlpatterns = []
urlpatterns.extend(common_urlpatterns) urlpatterns.extend(common_urlpatterns)

View File

@ -0,0 +1,19 @@
# Generated by Django 2.2.4 on 2019-10-07 14:39
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('main', '0016_merge_20190919_0954'),
]
operations = [
migrations.AddField(
model_name='feature',
name='route',
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, to='main.Page'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.4 on 2019-10-07 14:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('main', '0017_feature_route'),
]
operations = [
migrations.AddField(
model_name='feature',
name='source',
field=models.PositiveSmallIntegerField(choices=[(0, 'Mobile'), (1, 'Web'), (2, 'All')], default=0, verbose_name='Source'),
),
]

View File

@ -1,19 +1,23 @@
"""Main app models.""" """Main app models."""
from typing import Iterable
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes import fields as generic from django.contrib.contenttypes import fields as generic
from django.contrib.contenttypes.models import ContentType
from django.contrib.postgres.fields import JSONField from django.contrib.postgres.fields import JSONField
from django.db import models from django.db import models
from django.db.models import Q
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.contrib.contenttypes.models import ContentType
from advertisement.models import Advertisement from advertisement.models import Advertisement
from configuration.models import TranslationSettings
from location.models import Country from location.models import Country
from main import methods from main import methods
from review.models import Review from review.models import Review
from utils.models import (ProjectBaseMixin, TJSONField, from utils.models import (ProjectBaseMixin, TJSONField,
TranslatedFieldsMixin, ImageMixin) TranslatedFieldsMixin, ImageMixin, PlatformMixin)
from utils.querysets import ContentTypeQuerySetMixin from utils.querysets import ContentTypeQuerySetMixin
from configuration.models import TranslationSettings
# #
# #
@ -109,7 +113,6 @@ class SiteSettingsQuerySet(models.QuerySet):
class SiteSettings(ProjectBaseMixin): class SiteSettings(ProjectBaseMixin):
subdomain = models.CharField(max_length=255, db_index=True, unique=True, subdomain = models.CharField(max_length=255, db_index=True, unique=True,
verbose_name=_('Subdomain')) verbose_name=_('Subdomain'))
country = models.OneToOneField(Country, on_delete=models.PROTECT, country = models.OneToOneField(Country, on_delete=models.PROTECT,
@ -150,7 +153,8 @@ class SiteSettings(ProjectBaseMixin):
@property @property
def published_sitefeatures(self): def published_sitefeatures(self):
return self.sitefeature_set.filter(published=True) return self.sitefeature_set\
.filter(Q(published=True) and Q(feature__source__in=[PlatformMixin.WEB, PlatformMixin.ALL]))
@property @property
def site_url(self): def site_url(self):
@ -159,11 +163,27 @@ class SiteSettings(ProjectBaseMixin):
domain=settings.SITE_DOMAIN_URI) domain=settings.SITE_DOMAIN_URI)
class Feature(ProjectBaseMixin): class Page(models.Model):
"""Page model."""
page_name = models.CharField(max_length=255, unique=True)
advertisements = models.ManyToManyField(Advertisement)
class Meta:
"""Meta class."""
verbose_name = _('Page')
verbose_name_plural = _('Pages')
def __str__(self):
return f'{self.page_name}'
class Feature(ProjectBaseMixin, PlatformMixin):
"""Feature model.""" """Feature model."""
slug = models.CharField(max_length=255, unique=True) slug = models.CharField(max_length=255, unique=True)
priority = models.IntegerField(unique=True, null=True, default=None) priority = models.IntegerField(unique=True, null=True, default=None)
route = models.ForeignKey(Page, on_delete=models.PROTECT, null=True, default=None)
site_settings = models.ManyToManyField(SiteSettings, through='SiteFeature') site_settings = models.ManyToManyField(SiteSettings, through='SiteFeature')
class Meta: class Meta:
@ -181,6 +201,12 @@ class SiteFeatureQuerySet(models.QuerySet):
def published(self, switcher=True): def published(self, switcher=True):
return self.filter(published=switcher) return self.filter(published=switcher)
def by_country_code(self, country_code: str):
return self.filter(site_settings__country__code=country_code)
def by_sources(self, sources: Iterable[int]):
return self.filter(feature__source__in=sources)
class SiteFeature(ProjectBaseMixin): class SiteFeature(ProjectBaseMixin):
"""SiteFeature model.""" """SiteFeature model."""
@ -351,19 +377,3 @@ class Carousel(models.Model):
def model_name(self): def model_name(self):
if hasattr(self.content_object, 'establishment_type'): if hasattr(self.content_object, 'establishment_type'):
return self.content_object.establishment_type.name_translated return self.content_object.establishment_type.name_translated
class Page(models.Model):
"""Page model."""
page_name = models.CharField(max_length=255, unique=True)
advertisements = models.ManyToManyField(Advertisement)
class Meta:
"""Meta class."""
verbose_name = _('Page')
verbose_name_plural = _('Pages')
def __str__(self):
return f'{self.page_name}'

View File

@ -25,6 +25,8 @@ class SiteFeatureSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source='feature.id') id = serializers.IntegerField(source='feature.id')
slug = serializers.CharField(source='feature.slug') slug = serializers.CharField(source='feature.slug')
priority = serializers.IntegerField(source='feature.priority') priority = serializers.IntegerField(source='feature.priority')
route = serializers.CharField(source='feature.route.page_name')
source = serializers.IntegerField(source='feature.source')
class Meta: class Meta:
"""Meta class.""" """Meta class."""
@ -32,7 +34,9 @@ class SiteFeatureSerializer(serializers.ModelSerializer):
fields = ('main', fields = ('main',
'id', 'id',
'slug', 'slug',
'priority' 'priority',
'route',
'source'
) )

View File

@ -1,15 +0,0 @@
"""Main app urls."""
from django.urls import path
from main import views
app = 'main'
urlpatterns = [
path('determine-site/', views.DetermineSiteView.as_view(), name='determine-site'),
path('determine-location/', views.DetermineLocation.as_view(), name='determine-location'),
path('sites/', views.SiteListView.as_view(), name='site-list'),
path('site-settings/<subdomain>/', views.SiteSettingsView.as_view(), name='site-settings'),
path('awards/', views.AwardView.as_view(), name='awards_list'),
path('awards/<int:pk>/', views.AwardRetrieveView.as_view(), name='awards_retrieve'),
path('carousel/', views.CarouselListView.as_view(), name='carousel-list'),
]

View File

12
apps/main/urls/common.py Normal file
View File

@ -0,0 +1,12 @@
"""Main app urls."""
from django.urls import path
from main.views.common import *
app = 'main'
common_urlpatterns = [
path('awards/', AwardView.as_view(), name='awards_list'),
path('awards/<int:pk>/', AwardRetrieveView.as_view(), name='awards_retrieve'),
path('carousel/', CarouselListView.as_view(), name='carousel-list'),
path('determine-location/', DetermineLocation.as_view(), name='determine-location')
]

11
apps/main/urls/mobile.py Normal file
View File

@ -0,0 +1,11 @@
from main.urls.common import common_urlpatterns
from django.urls import path
from main.views.mobile import FeaturesView
urlpatterns = [
path('features/', FeaturesView.as_view(), name='features'),
]
urlpatterns.extend(common_urlpatterns)

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

@ -0,0 +1,11 @@
from main.urls.common import common_urlpatterns
from django.urls import path
from main.views.web import DetermineSiteView, SiteListView, SiteSettingsView
urlpatterns = [
path('determine-site/', DetermineSiteView.as_view(), name='determine-site'),
path('sites/', SiteListView.as_view(), name='site-list'),
path('site-settings/<subdomain>/', SiteSettingsView.as_view(), name='site-settings'), ]
urlpatterns.extend(common_urlpatterns)

View File

View File

@ -1,56 +1,9 @@
"""Main app views.""" """Main app views."""
from django.http import Http404
from rest_framework import generics, permissions from rest_framework import generics, permissions
from rest_framework.response import Response from rest_framework.response import Response
from main import methods, models, serializers from main import methods, models, serializers
from utils.serializers import EmptySerializer
from django.http import Http404
class DetermineSiteView(generics.GenericAPIView):
"""Determine user's site."""
permission_classes = (permissions.AllowAny,)
serializer_class = EmptySerializer
def get(self, request, *args, **kwargs):
user_ip = methods.get_user_ip(request)
country_code = methods.determine_country_code(user_ip)
url = methods.determine_user_site_url(country_code)
return Response(data={'url': url})
class DetermineLocation(generics.GenericAPIView):
"""Determine user's location."""
permission_classes = (permissions.AllowAny,)
serializer_class = EmptySerializer
def get(self, request, *args, **kwargs):
user_ip = methods.get_user_ip(request)
longitude, latitude = methods.determine_coordinates(user_ip)
city = methods.determine_user_city(user_ip)
if longitude and latitude and city:
return Response(data={'latitude': latitude, 'longitude': longitude, 'city': city})
else:
raise Http404
class SiteSettingsView(generics.RetrieveAPIView):
"""Site settings View."""
lookup_field = 'subdomain'
permission_classes = (permissions.AllowAny,)
queryset = models.SiteSettings.objects.all()
serializer_class = serializers.SiteSettingsSerializer
class SiteListView(generics.ListAPIView):
"""Site settings View."""
pagination_class = None
permission_classes = (permissions.AllowAny,)
queryset = models.SiteSettings.objects.with_country()
serializer_class = serializers.SiteSerializer
# #
@ -89,6 +42,7 @@ class SiteListView(generics.ListAPIView):
# class SiteFeaturesRUDView(SiteFeaturesViewMixin, # class SiteFeaturesRUDView(SiteFeaturesViewMixin,
# generics.RetrieveUpdateDestroyAPIView): # generics.RetrieveUpdateDestroyAPIView):
# """Site features RUD.""" # """Site features RUD."""
from utils.serializers import EmptySerializer
class AwardView(generics.ListAPIView): class AwardView(generics.ListAPIView):
@ -111,3 +65,20 @@ class CarouselListView(generics.ListAPIView):
serializer_class = serializers.CarouselListSerializer serializer_class = serializers.CarouselListSerializer
permission_classes = (permissions.AllowAny,) permission_classes = (permissions.AllowAny,)
pagination_class = None pagination_class = None
class DetermineLocation(generics.GenericAPIView):
"""Determine user's location."""
permission_classes = (permissions.AllowAny,)
serializer_class = EmptySerializer
def get(self, request, *args, **kwargs):
user_ip = methods.get_user_ip(request)
longitude, latitude = methods.determine_coordinates(user_ip)
city = methods.determine_user_city(user_ip)
if longitude and latitude and city:
return Response(data={'latitude': latitude, 'longitude': longitude, 'city': city})
else:
raise Http404

16
apps/main/views/mobile.py Normal file
View File

@ -0,0 +1,16 @@
from rest_framework import generics, permissions
from main import models, serializers
from utils.models import PlatformMixin
class FeaturesView(generics.ListAPIView):
pagination_class = None
permission_classes = (permissions.AllowAny,)
serializer_class = serializers.SiteFeatureSerializer
def get_queryset(self):
return models.SiteFeature.objects\
.prefetch_related('feature', 'feature__route') \
.by_country_code(self.request.country_code) \
.by_sources([PlatformMixin.ALL, PlatformMixin.MOBILE])

38
apps/main/views/web.py Normal file
View File

@ -0,0 +1,38 @@
from typing import Iterable
from rest_framework import generics, permissions
from utils.serializers import EmptySerializer
from rest_framework.response import Response
from main import methods, models, serializers
class DetermineSiteView(generics.GenericAPIView):
"""Determine user's site."""
permission_classes = (permissions.AllowAny,)
serializer_class = EmptySerializer
def get(self, request, *args, **kwargs):
user_ip = methods.get_user_ip(request)
country_code = methods.determine_country_code(user_ip)
url = methods.determine_user_site_url(country_code)
return Response(data={'url': url})
class SiteSettingsView(generics.RetrieveAPIView):
"""Site settings View."""
lookup_field = 'subdomain'
permission_classes = (permissions.AllowAny,)
queryset = models.SiteSettings.objects.all()
serializer_class = serializers.SiteSettingsSerializer
class SiteListView(generics.ListAPIView):
"""Site settings View."""
pagination_class = None
permission_classes = (permissions.AllowAny,)
queryset = models.SiteSettings.objects.with_country()
serializer_class = serializers.SiteSerializer

View File

@ -4,6 +4,7 @@ app_name = 'mobile'
urlpatterns = [ urlpatterns = [
path('establishments/', include('establishment.urls.mobile')), path('establishments/', include('establishment.urls.mobile')),
path('main/', include('main.urls.mobile'))
# path('account/', include('account.urls.web')), # path('account/', include('account.urls.web')),
# path('advertisement/', include('advertisement.urls.web')), # path('advertisement/', include('advertisement.urls.web')),
# path('collection/', include('collection.urls.web')), # path('collection/', include('collection.urls.web')),

View File

@ -27,7 +27,7 @@ urlpatterns = [
path('notifications/', include(('notification.urls.web', "notification"), namespace='notification')), path('notifications/', include(('notification.urls.web', "notification"), namespace='notification')),
path('partner/', include('partner.urls.web')), path('partner/', include('partner.urls.web')),
path('location/', include('location.urls.web')), path('location/', include('location.urls.web')),
path('main/', include('main.urls')), path('main/', include('main.urls.web')),
path('recipes/', include('recipe.urls.web')), path('recipes/', include('recipe.urls.web')),
path('translation/', include('translation.urls')), path('translation/', include('translation.urls')),
path('comments/', include('comment.urls.web')), path('comments/', include('comment.urls.web')),