diff --git a/apps/news/models.py b/apps/news/models.py index efa0b566..a4aae03a 100644 --- a/apps/news/models.py +++ b/apps/news/models.py @@ -5,6 +5,8 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ from rest_framework.reverse import reverse from utils.models import BaseAttributes, TJSONField, TranslatedFieldsMixin +from django.contrib.contenttypes.models import ContentType +from rating.models import Rating from random import sample as random_sample @@ -27,6 +29,9 @@ class NewsType(models.Model): class NewsQuerySet(models.QuerySet): """QuerySet for model News""" + def rating_value(self): + return self.annotate(rating=models.Count('ratings__ip', distinct=True)) + def with_base_related(self): """Return qs with related objects.""" return self.select_related('news_type', 'country').prefetch_related('tags') @@ -132,6 +137,8 @@ class News(BaseAttributes, TranslatedFieldsMixin): verbose_name=_('country')) tags = generic.GenericRelation(to='main.MetaDataContent') + ratings = generic.GenericRelation(Rating) + objects = NewsQuerySet.as_manager() class Meta: diff --git a/apps/news/views.py b/apps/news/views.py index 081f5a62..61a57251 100644 --- a/apps/news/views.py +++ b/apps/news/views.py @@ -1,7 +1,7 @@ """News app views.""" from rest_framework import generics, permissions from news import filters, models, serializers - +from rating.tasks import add_rating class NewsMixinView: """News mixin.""" @@ -34,7 +34,6 @@ class NewsDetailView(NewsMixinView, generics.RetrieveAPIView): """Override get_queryset method.""" return super().get_queryset().with_extended_related() - class NewsTypeListView(generics.ListAPIView): """NewsType list view.""" @@ -76,3 +75,7 @@ class NewsBackOfficeRUDView(NewsBackOfficeMixinView, serializer_class = serializers.NewsBackOfficeDetailSerializer + def get(self, request, pk, *args, **kwargs): + add_rating(remote_addr=request.META.get('REMOTE_ADDR'), + pk=pk, model='news', app_label='news') + return self.retrieve(request, *args, **kwargs) diff --git a/apps/rating/__init__.py b/apps/rating/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/rating/admin.py b/apps/rating/admin.py new file mode 100644 index 00000000..151f406b --- /dev/null +++ b/apps/rating/admin.py @@ -0,0 +1,11 @@ +from django.contrib import admin +from rating import models +from rating import tasks + +@admin.register(models.Rating) +class RatingAdmin(admin.ModelAdmin): + """Rating type admin conf.""" + list_display = ['name', 'ip'] + list_display_links = ['name'] + + diff --git a/apps/rating/apps.py b/apps/rating/apps.py new file mode 100644 index 00000000..6f17a343 --- /dev/null +++ b/apps/rating/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class RatingConfig(AppConfig): + name = 'rating' diff --git a/apps/rating/migrations/0001_initial.py b/apps/rating/migrations/0001_initial.py new file mode 100644 index 00000000..03165670 --- /dev/null +++ b/apps/rating/migrations/0001_initial.py @@ -0,0 +1,28 @@ +# Generated by Django 2.2.4 on 2019-10-02 11:32 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ] + + operations = [ + migrations.CreateModel( + name='Rating', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('object_id', models.PositiveIntegerField()), + ('ip', models.GenericIPAddressField(verbose_name='ip')), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ], + options={ + 'verbose_name': 'rating', + }, + ), + ] diff --git a/apps/rating/migrations/__init__.py b/apps/rating/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/rating/models.py b/apps/rating/models.py new file mode 100644 index 00000000..61e4378d --- /dev/null +++ b/apps/rating/models.py @@ -0,0 +1,19 @@ +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class Rating(models.Model): + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey('content_type', 'object_id') + ip = models.GenericIPAddressField(verbose_name=_('ip')) + + @property + def name(self): + # Check if Generic obj has name or title + if hasattr(self.content_object, 'name'): + return self.content_object.name + if hasattr(self.content_object, 'title'): + return self.content_object.title_translated diff --git a/apps/rating/tasks.py b/apps/rating/tasks.py new file mode 100644 index 00000000..0e176910 --- /dev/null +++ b/apps/rating/tasks.py @@ -0,0 +1,22 @@ +from datetime import timedelta +from celery import task +from rating.models import Rating +from django.contrib.contenttypes.models import ContentType + + +def add_rating(remote_addr, pk, model, app_label): + add.apply_async( + (remote_addr, pk, model, app_label), countdown=60 * 60 + ) + + +@task +def add(remote_addr, pk, model, app_label): + rating = Rating() + rating.ip = remote_addr + rating.object_id = pk + rating.content_type = ContentType.objects.get(app_label=app_label, model=model) + rating.save() + + + diff --git a/apps/rating/tests.py b/apps/rating/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/apps/rating/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/rating/views.py b/apps/rating/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/apps/rating/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/project/settings/base.py b/project/settings/base.py index d145d1ba..cec6a4d1 100644 --- a/project/settings/base.py +++ b/project/settings/base.py @@ -71,6 +71,7 @@ PROJECT_APPS = [ 'review.apps.ReviewConfig', 'comment.apps.CommentConfig', 'favorites.apps.FavoritesConfig', + 'rating.apps.RatingConfig', ] EXTERNAL_APPS = [