You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

410 lines
16 KiB

from datetime import timedelta
from paymentwall import Pingback
from django.contrib.auth import get_user_model
from django.contrib.auth.decorators import login_required
from django.db.models import Q
from django.http import JsonResponse, Http404
from django.shortcuts import get_object_or_404
from django.template import loader, Context, Template
from django.views.generic import View, CreateView, DetailView, ListView, TemplateView
from django.utils.cache import add_never_cache_headers
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from django.utils.translation import gettext as _
from django.utils.timezone import now
from apps.payment.models import AuthorBalance, CoursePayment
from .models import Course, Like, Lesson, CourseComment, LessonComment
from .filters import CourseFilter
User = get_user_model()
@login_required
@csrf_exempt
@require_http_methods(['POST'])
def likes(request, course_id):
try:
course = Course.objects.prefetch_related('likes').get(id=course_id)
except Course.DoesNotExist:
return JsonResponse({
'success': False,
'errors': ['Course with id f{course_id} not found']
}, status=400)
else:
course_user_likes = course.likes.filter(user=request.user)
if course_user_likes.exists():
is_liked = False
for _course in course_user_likes.all():
course.likes.remove(_course)
course_user_likes.delete()
else:
is_liked = True
course.likes.add(Like.objects.create(user=request.user))
count = course.likes.count()
return JsonResponse({
"success": True,
"likes_count": count,
"is_liked": is_liked,
})
@login_required
@csrf_exempt
@require_http_methods(['POST'])
def coursecomment(request, course_id):
try:
course = Course.objects.get(id=course_id)
except Course.DoesNotExist:
return JsonResponse({
'success': False,
'errors': ['Course with id f{course_id} not found']
}, status=400)
else:
reply_to = request.POST.get('reply_id', 0)
comment = request.POST.get('comment', '')
if not comment:
return JsonResponse({
'success': False,
'errors': ['Comment can not be empty']
}, status=400)
if not int(reply_to):
coursecomment = CourseComment.objects.create(
author=request.user,
content=comment,
course=course,
)
else:
try:
_coursecomment = CourseComment.objects.get(id=reply_to)
except CourseComment.DoesNotExist:
return JsonResponse({
'success': False,
'errors': ['CourseComment with id f{reply_to} not found']
}, status=400)
else:
coursecomment = CourseComment.objects.create(
author=request.user,
content=comment,
course=course,
parent=_coursecomment,
)
ctx = {'node': coursecomment, 'user': request.user}
html = loader.render_to_string('templates/blocks/comment.html', ctx)
return JsonResponse({
'success': True,
'comment': html,
})
@login_required
@csrf_exempt
@require_http_methods(['POST'])
def lessoncomment(request, lesson_id):
try:
lesson = Lesson.objects.get(id=lesson_id)
except Lesson.DoesNotExist:
return JsonResponse({
'success': False,
'errors': ['Lesson with id f{lesson_id} not found']
}, status=400)
else:
reply_to = request.POST.get('reply_id', 0)
comment = request.POST.get('comment', '')
if not comment:
return JsonResponse({
'success': False,
'errors': ['Comment can not be empty']
}, status=400)
if not int(reply_to):
lessoncomment = LessonComment.objects.create(
author=request.user,
content=comment,
lesson=lesson,
)
else:
try:
_lessoncomment = LessonComment.objects.get(id=reply_to)
except LessonComment.DoesNotExist:
return JsonResponse({
'success': False,
'errors': ['LessonComment with id f{reply_to} not found']
}, status=400)
else:
lessoncomment = LessonComment.objects.create(
author=request.user,
content=comment,
lesson=lesson,
parent=_lessoncomment,
)
ctx = {'node': lessoncomment, 'user': request.user}
html = loader.render_to_string('templates/blocks/comment.html', ctx)
return JsonResponse({
'success': True,
'comment': html,
})
@method_decorator(login_required, name='dispatch')
class CourseOnModerationView(TemplateView):
template_name = 'course/course_on_moderation.html'
@method_decorator(login_required, name='dispatch')
class CourseEditView(TemplateView):
template_name = 'course/course_edit.html'
def get(self, request, pk=None, lesson=None):
drafts = Course.objects.filter(
author=request.user, status=Course.DRAFT
)
if pk:
self.object = get_object_or_404(Course, pk=pk)
elif drafts.exists():
self.object = drafts.last()
else:
self.object = Course.objects.create(
author=request.user,
)
if request.user != self.object.author and request.user.role != User.ADMIN_ROLE:
raise Http404
return super().get(request)
def get_context_data(self):
context = super().get_context_data()
context['live'] = 'false'
context['object'] = self.object
return context
# @method_decorator(login_required, name='dispatch')
class CourseView(DetailView):
model = Course
context_object_name = 'course'
template_name = 'course/course.html'
only_lessons = False
def get(self, request, *args, **kwargs):
response = super().get(request, *args, **kwargs)
context = self.get_context_data()
# если это не админ или автор
if not context.get('has_full_access'):
# если это не опубл курс или это страница уроков, курс платный, а юзер не оплатил курс - 404
if (self.object.status != Course.PUBLISHED) or \
(self.only_lessons and self.object.price and not context.get('paid')):
raise Http404
return response
# ((self.object.status != Course.PUBLISHED and request.user.role != User.ADMIN_ROLE) or
# (self.object.status != Course.PUBLISHED and request.user.role != User.AUTHOR_ROLE and self.object.author != request.user)):
def get_object(self, queryset=None):
if queryset is None:
queryset = self.get_queryset()
pk = self.kwargs.get(self.pk_url_kwarg)
slug = self.kwargs.get(self.slug_url_kwarg)
if pk is not None:
queryset = queryset.filter(pk=pk)
if slug is not None and (pk is None or self.query_pk_and_slug):
slug_field = self.get_slug_field()
queryset = queryset.filter(**{'%s__iexact' % slug_field: slug})
if pk is None and slug is None:
raise AttributeError("Generic detail view %s must be called with "
"either an object pk or a slug."
% self.__class__.__name__)
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404(_("No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.user.is_authenticated:
context['next'] = self.request.GET.get('next', None)
# берем последнюю оплату курса
payments = self.object.payments.filter(
user=self.request.user,
status__in=[
Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_GOODWILL,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
])
payment = payments.filter(access_expire__gte=now().date()).order_by('-access_expire').first()
context['payment'] = payment
context['access_duration'] = ((payment.access_expire - now().date()).days + 1) if payment else self.object.access_duration
context['paid'] = bool(payment)
context['can_buy_again'] = bool(self.object.price) and (context['access_duration'] <= 7 if payment else
payments.filter(access_expire__lt=now().date()).exists())
context['pending'] = self.object.payments.filter(
user=self.request.user,
status=Pingback.PINGBACK_TYPE_RISK_UNDER_REVIEW,
).exists()
context['only_lessons'] = self.only_lessons
if self.only_lessons:
context['lessons'] = self.object.lessons.order_by('position')
context['is_owner'] = self.object.author == self.request.user
context['is_admin'] = self.request.user.role == User.ADMIN_ROLE
context['has_full_access'] = context['is_owner'] or context['is_admin']
context['course_price'] = self.object.price / 2 if context.get('can_buy_again') else self.object.price
return context
def get_queryset(self):
queryset = super().get_queryset().select_related(
'author', 'category',
).prefetch_related(
'likes', 'materials', 'content',
)
return queryset
class CoursesView(ListView):
model = Course
context_object_name = 'course_items'
paginate_by = 12
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
if request.is_ajax():
context = self.get_context_data()
template_name = self.get_template_names()
html = loader.render_to_string(
template_name, context, request=request)
is_paginated = context.get('is_paginated')
if is_paginated:
page_obj = context.get('page_obj')
prev_url = request.path + '?page=' + \
str(page_obj.previous_page_number()
) if page_obj.has_previous() else None
next_url = request.path + '?page=' + \
str(page_obj.next_page_number()
) if page_obj.has_next() else None
else:
prev_url = None
next_url = None
response = JsonResponse({
'success': True,
'content': html,
'prev_url': prev_url,
'next_url': next_url,
})
add_never_cache_headers(response)
return response
else:
return super().get(request, args, kwargs)
def get_queryset(self):
queryset = super().get_queryset().select_related(
'author', 'category', 'cover',
).prefetch_related(
'likes', 'materials', 'content',
).filter(status=Course.PUBLISHED)
filtered = CourseFilter(self.request.GET, queryset=queryset)
return filtered.qs
def get_context_data(self):
context = super().get_context_data()
filtered = CourseFilter(self.request.GET)
context.update(filtered.data)
context['course_items'] = Course.shuffle(context.get('course_items'))
context['ages'] = Course.AGE_CHOICES[1:]
age = context.get('age')
if age and age[0]:
age = int(age[0])
context['age'] = [age]
context['age_name'] = dict(Course.AGE_CHOICES).get(age, '')
else:
context['age_name'] = ''
if self.request.user.is_authenticated:
can_buy_again_courses = list(CoursePayment.objects.filter(user=self.request.user,
status__in=CoursePayment.PW_PAID_STATUSES,
access_expire__lte=now().date() + timedelta(7)).values_list('course_id', flat=True))
for course in context['course_items']:
if course.id in can_buy_again_courses:
course.buy_again_price = course.price / 2
return context
def get_template_names(self):
if self.request.is_ajax():
return 'course/course_items.html'
return 'course/courses.html'
@method_decorator(login_required, name='dispatch')
class LessonView(DetailView):
model = Lesson
context_object_name = 'lesson'
template_name = 'course/lesson.html'
def get(self, request, *args, **kwargs):
response = super().get(request, *args, **kwargs)
paid = request.user.is_authenticated and self.object.course.payments.filter(
user=self.request.user,
status__in=[
Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_GOODWILL,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
],
access_expire__gte=now().date(),
).exists()
# если это не автор или админ
if not (request.user.is_authenticated and
(request.user.role == User.ADMIN_ROLE or self.object.course.author == request.user)):
# если курс не опубликован или он платный и не оплачен - 404
if self.object.course.status != Course.PUBLISHED or (self.object.course.price and not paid):
raise Http404
return response
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['next'] = self.request.GET.get('next', None)
lessons = list(self.object.course.lessons.values_list('id', flat=True))
index = lessons.index(self.object.id)
context['next_lesson'] = lessons[index + 1] if index < len(lessons) - 1 else None
return context
class SearchView(CoursesView):
template_name = 'course/result.html'
def get_queryset(self):
search_query = self.request.GET.get('q', None)
queryset = super().get_queryset()
if search_query:
query = Q(title__icontains=search_query) | Q(
short_description__icontains=search_query)
queryset = queryset.filter(query)
else:
queryset = queryset.none()
return queryset
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['q'] = self.request.GET.get('q', None) or ''
return context
if 'is_paginated' in context and context['is_paginated']:
page_obj = context.get('page_obj')
context['page'] = page_obj.number
context['next_page'] = str(
page_obj.next_page_number()) if page_obj.has_next() else None
else:
context['page'] = 1
context['next_page'] = None
return context
def get_template_names(self):
if self.request.is_ajax():
return 'course/course_items.html'
return 'course/result.html'