LIL-642 Не учитывать голоса удаленных пользователей (is_active false) в конкурсе

remotes/origin/hotfix/LIL-661
gzbender 7 years ago
parent 2cab36e8df
commit 66a0fef6bb
  1. 15
      api/v1/serializers/contest.py
  2. 21
      api/v1/serializers/course.py
  3. 24
      api/v1/views.py
  4. 7
      apps/content/models.py
  5. 2
      apps/content/templates/content/contest_work.html
  6. 8
      apps/content/views.py
  7. 2
      apps/school/templates/summer/promo.html
  8. 2
      project/templates/blocks/about.html
  9. 2
      project/urls.py
  10. 8
      web/src/components/ContestWorks.vue
  11. 9
      web/src/components/blocks/Likes.vue

@ -1,3 +1,4 @@
from django.contrib.auth import get_user_model
from rest_framework import serializers
from api.v1.serializers.content import ContentSerializer, ContentCreateSerializer, ImageObjectSerializer
@ -5,6 +6,8 @@ from api.v1.serializers.mixins import DispatchContentMixin
from apps.content.models import (Contest, ContestWork)
User = get_user_model()
class ContestSerializer(serializers.ModelSerializer):
cover = ImageObjectSerializer()
@ -12,7 +15,8 @@ class ContestSerializer(serializers.ModelSerializer):
class Meta:
model = Contest
fields = '__all__'
fields = ['title', 'description', 'slug', 'cover',
'date_start', 'date_end', 'active', 'content', 'finished']
class ContestCreateSerializer(DispatchContentMixin, serializers.ModelSerializer):
@ -52,11 +56,14 @@ class ContestWorkSerializer(serializers.ModelSerializer):
'created_at', 'likes', 'user_liked', 'img_width', 'img_height']
def get_likes(self, instance):
return instance.likes.count()
return instance.likes.filter(user__is_active=True).count()
def get_user_liked(self, instance):
user = self.context['request'].user
return instance.likes.filter(user=user).exists() if user.is_authenticated else False
# FIXME
user = self.context['request'].query_params.get('current_user')
if user:
user = User.objects.get(pk=user)
return instance.likes.filter(user=user).exists() if user else False
class ContestWorkCreateSerializer(serializers.ModelSerializer):

@ -71,14 +71,10 @@ class LikeSerializer(serializers.ModelSerializer):
class LikeCreateSerializer(serializers.ModelSerializer):
OBJ_TYPE_CONTEST_WORK = 'contest_work'
obj_type = serializers.CharField(required=True)
obj_id = serializers.IntegerField(required=True)
class Meta:
model = Like
fields = ['user', 'obj_type', 'obj_id']
fields = ['user']
def create(self, validated_data):
# FIXME
@ -86,20 +82,9 @@ class LikeCreateSerializer(serializers.ModelSerializer):
user = validated_data.get('user')
else:
user = self.context['request'].user
if not user: # FIXME and user.is_authenticated):
return Like()
obj_type = validated_data.pop('obj_type')
obj_id = validated_data.pop('obj_id')
client_ip, is_routable = get_client_ip(self.context['request'])
if obj_type == self.OBJ_TYPE_CONTEST_WORK:
contest_work = ContestWork.objects.get(pk=obj_id)
# FIXME in prod: fixed
if contest_work.user == user or contest_work.likes.filter(user=user).exists():
# if contest_work.likes.filter(user=user).exists():
return Like()
like = Like.objects.create(user=user, ip=client_ip)
contest_work.likes.add(like)
return like
like = Like.objects.create(user=user, ip=client_ip)
return like
def to_representation(self, instance):
return LikeSerializer(instance, context=self.context).to_representation(instance)

@ -131,6 +131,8 @@ class MaterialViewSet(ExtendedModelViewSet):
class LikeViewSet(ExtendedModelViewSet):
OBJ_TYPE_CONTEST_WORK = 'contest_work'
queryset = Like.objects.select_related('user').all()
serializer_class = LikeCreateSerializer
serializer_class_map = {
@ -143,6 +145,28 @@ class LikeViewSet(ExtendedModelViewSet):
# FIXME
authentication_classes = []
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
obj_type = request.data.get('obj_type')
obj_id = request.data.get('obj_id')
user = serializer.validated_data.get('user')
if not user.is_active: # FIXME and user.is_authenticated):
return Response(status=status.HTTP_403_FORBIDDEN)
if obj_type == self.OBJ_TYPE_CONTEST_WORK:
contest_work = ContestWork.objects.get(pk=obj_id)
if contest_work.user == user:
return Response({'error': u'Нельзя голосовать за свою работу'}, status=status.HTTP_400_BAD_REQUEST)
if contest_work.likes.filter(user=user).exists():
return Response({'error': u'Вы уже голосовали за эту работу'}, status=status.HTTP_400_BAD_REQUEST)
if contest_work.contest.finished:
return Response({'error': u'Голосование закончено'}, status=status.HTTP_400_BAD_REQUEST)
instance = serializer.save()
if obj_type == self.OBJ_TYPE_CONTEST_WORK:
contest_work.likes.add(instance)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
class CategoryViewSet(ExtendedModelViewSet):
queryset = Category.objects.order_by('-id')

@ -1,7 +1,10 @@
from datetime import datetime, time
from urllib.parse import urlparse
from django.conf import settings
from django.db import models
from django.contrib.auth import get_user_model
from django.urls import reverse_lazy
from django.utils import timezone
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToCover
@ -167,6 +170,10 @@ class Contest(models.Model):
active = models.BooleanField(default=True)
# TODO? baner
@property
def finished(self):
return datetime(2018, 8, 29, 21) < timezone.now()
def save(self, *args, **kwargs):
if self.active:
Contest.objects.filter(active=True).update(active=False)

@ -35,7 +35,7 @@
</div>
<div class="contest-work__likes">
<likes obj-type="contest_work" obj-id="{{ contest_work.id }}"
{% if user_liked %}:user-liked="true"{% endif %} likes="{{ contest_work.likes.count }}"></likes>
{% if user_liked %}:user-liked="true"{% endif %} likes="{{ likes_count }}"></likes>
</div>
</div>
</div>

@ -14,10 +14,11 @@ from apps.course.models import ContestWorkComment
@method_decorator(login_required, name='dispatch')
class ContestEditView(TemplateView):
template_name = 'content/contest_edit.html'
query_pk_and_slug = True
def get(self, request, pk=None, lesson=None):
if pk:
self.object = get_object_or_404(Contest, pk=pk)
def get(self, request, slug=None, lesson=None):
if slug:
self.object = get_object_or_404(Contest, slug=slug)
else:
self.object = Contest()
@ -58,6 +59,7 @@ class ContestWorkView(DetailView):
context['user_liked'] = self.object.likes.filter(user=self.request.user).exists() \
if self.request.user.is_authenticated else False
context['likes_count'] = self.object.likes.filter(user__is_active=True).count()
return context

@ -46,7 +46,7 @@
<use xlink:href="{% static 'img/sprite.svg' %}#icon-subjects"></use>
</svg>
</div>
<div class="school__title">12 уроков</div>
<div class="school__title">7 дисциплин</div>
<div class="school__text">В разных техниках</div>
</div>
<div class="school__col">

@ -30,7 +30,7 @@
<use xlink:href="{% static 'img/sprite.svg' %}#icon-subjects"></use>
</svg>
</div>
<div class="school__title">12 уроков</div>
<div class="school__title">7 дисциплин</div>
<div class="school__text">В разных техниках</div>
</div>
<div class="school__col">

@ -84,7 +84,7 @@ urlpatterns = [
path('school/', include(('apps.school.urls', 'school'))),
path('test', TemplateView.as_view(template_name='templates/lilcity/test.html'), name='test'),
path('contest/create', ContestEditView.as_view(), name='contest_create'),
path('contest/<int:pk>/edit', ContestEditView.as_view(), name='contest_edit'),
path('contest/<str:slug>/edit', ContestEditView.as_view(), name='contest_edit'),
path('contest/<str:slug>/', ContestView.as_view(), name='contest'),
path('contest-work/<int:pk>/', ContestWorkView.as_view(), name='contest_work'),
path('contest-work/<int:contest_work_id>/comment', contest_work_comment, name='contest_work_comment'),

@ -61,8 +61,12 @@
methods: {
load() {
this.loading = true;
api.get(`/api/v1/contest-works/?contest=${this.contestId}&page=${this.page}`)
.then((response) => {
api.get(`/api/v1/contest-works/?contest=${this.contestId}&page=${this.page}&current_user=${this.$root.store.user.id}`, {
headers: {
'Authorization': `Token ${this.$root.store.accessToken}`,
}
})
.then(response => {
this.loading = false;
this.loaded = true;
if(this.page > 1){

@ -10,6 +10,7 @@
<script>
import {api} from "../../js/modules/api";
import {showNotification} from "../../js/modules/notification";
export default {
name: 'likes',
@ -22,6 +23,7 @@
},
methods: {
addLike(event) {
this.$emit('like');
if(this.userLikedProp){
return;
}
@ -36,12 +38,17 @@
'Authorization': `Token ${this.$root.store.accessToken}`,
}
})
.then((response) => {
.then(response => {
if (response.data && response.data.id) {
this.userLikedProp = true;
this.likesProp += 1;
this.$emit('liked');
}
})
.catch(({response}) => {
if(response.status == 400 && response.data.error){
showNotification('error', response.data.error);
}
});
}
}

Loading…
Cancel
Save