from io import BytesIO from PIL import Image from uuid import uuid4 from os.path import splitext from datetime import timedelta from paymentwall import Pingback from django.conf import settings from django.contrib.auth import login from django.core.exceptions import ValidationError from django.shortcuts import render, reverse, redirect from django.views import View from django.views.generic import DetailView, UpdateView, TemplateView, FormView from django.contrib import messages from django.contrib.auth import get_user_model from django.contrib.auth.decorators import login_required, permission_required from django.contrib.auth.hashers import check_password, make_password from django.http import Http404 from django.db.models import F, Func, Sum, Min, Max from django.urls import reverse_lazy from django.utils.decorators import method_decorator from django.utils.timezone import now from apps.auth.tokens import verification_email_token from apps.course.models import Course from apps.notification.utils import send_email from apps.school.models import SchoolSchedule from apps.payment.models import AuthorBalance, CoursePayment, SchoolPayment from apps.user.models import AuthorRequest, EmailSubscription, SubscriptionCategory from .forms import AuthorRequesForm, UserEditForm, WithdrawalForm User = get_user_model() @login_required def resend_email_verify(request): token = verification_email_token.make_token(request.user) url = request.scheme + '://' + request.get_host() + str(reverse_lazy('lilcity:verification-email', args=[token, request.user.id])) send_email('Вы успешно прошли регистрацию', request.user.email, "notification/email/verification_email.html", url=url) messages.info(request, 'Письмо подтверждения отправлено.') return redirect('user-edit-profile') @method_decorator(login_required, name='dispatch') class ProfileView(TemplateView): model = User template_name = 'user/profile.html' def get(self, request, *args, **kwargs): self.object = self.request.user context = self.get_context_data(object=self.object) return self.render_to_response(context) def get_context_data(self, object): context = super().get_context_data() context['user'] = self.request.user context['published'] = Course.objects.filter( author=self.object, ) context['is_author'] = context['published'] or self.request.user.role == User.AUTHOR_ROLE context['paid'] = Course.objects.filter( payments__in=CoursePayment.objects.filter( user=self.object, status__in=[ Pingback.PINGBACK_TYPE_REGULAR, Pingback.PINGBACK_TYPE_GOODWILL, Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, ], ), ).distinct() school_payment = SchoolPayment.objects.filter( user=self.object, date_start__lte=now(), date_end__gte=now(), status__in=[ Pingback.PINGBACK_TYPE_REGULAR, Pingback.PINGBACK_TYPE_GOODWILL, Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, ], ) context['is_school_purchased'] = school_payment.exists() if context['is_school_purchased']: school_schedules_purchased = school_payment.annotate( joined_weekdays=Func(F('weekdays'), function='unnest',) ).values_list('joined_weekdays', flat=True).distinct() aggregated = school_payment.aggregate(Sum('amount'), Min('date_start'), Max('date_end'),) context['school_purchased_weekdays'] = '-'.join(map(lambda wd: SchoolSchedule.WEEKDAY_SHORT_NAMES[wd-1], set(sorted(school_schedules_purchased)))) context['school_purchased_price'] = aggregated.get('amount__sum') or 0 context['school_purchased_dates'] = [aggregated.get('date_start__min'), aggregated.get('date_end__max')] context['profile'] = True return context class UserView(DetailView): model = User template_name = 'user/author_profile.html' query_pk_and_slug = True def get_context_data(self, object): context = super().get_context_data() context['published'] = Course.objects.filter( author=self.object, status=Course.PUBLISHED, ) return context class SubscribeView(View): def post(self, request, pk=None, **kwargs): refferer = request.META.get('HTTP_REFERER') if request.user.is_authenticated: messages.info(request, 'Вы уже подписаны на рассылки.') return redirect(refferer) email = request.POST.get('email', None) if email: if EmailSubscription.objects.filter(email=email).exists(): messages.error(request, 'Вы уже подписаны!') return redirect(refferer) email_subscription = EmailSubscription.objects.create( email=email, ) email_subscription.categories.set( SubscriptionCategory.objects.filter(auto_add=True) ) messages.info(request, 'Вы подписаны на новости.') return redirect('subscribe-success') else: messages.error(request, 'Введите адрес электронной почты.') return redirect(refferer) @method_decorator(login_required, name='dispatch') class NotificationEditView(TemplateView): template_name = 'user/notification-settings.html' def get(self, request, pk=None): return super().get(request) def post(self, request, pk=None): categories = request.POST.getlist('category', []) request.user.email_subscription.categories.set( SubscriptionCategory.objects.filter(id__in=categories) ) return redirect('user-edit-notifications', request.user.id) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['subscription_categories'] = SubscriptionCategory.objects.all() return context @method_decorator(login_required, name='dispatch') class PaymentHistoryView(FormView): template_name = 'user/payment-history.html' form_class = WithdrawalForm def get(self, request, pk=None): return super().get(request) def post(self, request, pk=None): form = self.get_form() if not settings.DEBUG and AuthorBalance.objects.filter(created_at__gte=now() - timedelta(days=30)).exists(): messages.error(request, 'Запрос на вывод средств можно сделать только один раз в 30 дней.') return self.form_invalid(form) if form.is_valid(): if request.user.balance < form.cleaned_data['amount']: form.errors['amount'] = 'Сумма для вывода не может быть меньше средств на счету' return self.form_invalid(form) AuthorBalance.objects.create( author=request.user, type=AuthorBalance.OUT, amount=form.cleaned_data['amount'], status=AuthorBalance.PENDING, card=form.cleaned_data['card'], ) return self.form_valid(form) else: return self.form_invalid(form) def get_success_url(self): success_url = reverse_lazy('user-edit-payments', args=[self.request.user.id]) return success_url @method_decorator(login_required, name='dispatch') class ProfileEditView(UpdateView): model = User template_name = 'user/profile-settings.html' form_class = UserEditForm def get_object(self, queryset=None): return self.request.user def dispatch(self, request, *args, **kwargs): self.object = self.get_object() return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['is_teacher'] = self.object.role == User.TEACHER_ROLE return context def post(self, request, *args, **kwargs): # it's magic *-*-*-*-* if 'photo' in request.FILES: photo_fp = request.FILES.pop('photo')[0] fname = photo_fp.name photo = Image.open(photo_fp) lowest_side = min(photo.size) horizontal_padding = (lowest_side - photo.size[0]) / 2 vertical_padding = (lowest_side - photo.size[1]) / 2 photo = photo.crop( ( -horizontal_padding, -vertical_padding, photo.size[0] + horizontal_padding, photo.size[1] + vertical_padding ) ) if photo.size[0] > 512: photo = photo.resize((512, 512,)) buffer = BytesIO() ext = splitext(fname)[1][1:].upper() if ext == 'JPG': ext = 'JPEG' photo.save(buffer, ext) fname = str(uuid4()) + '.' + ext.lower() self.object.photo.save(fname, buffer) buffer.close() if not request.POST._mutable: request.POST._mutable = True old_password = request.POST.pop('old_password')[0] new_password1 = request.POST.pop('new_password1')[0] new_password2 = request.POST.pop('new_password2')[0] if old_password: if request.user.check_password(old_password) and new_password1 == new_password2: request.user.set_password(new_password1) request.user.save() login(request, request.user) else: messages.error(request, 'Неверный пароль.') messages.info(request, 'Данные сохранены.') return super().post(request, *args, **kwargs) def get_success_url(self): return reverse('user-edit-profile') class AuthorRequestView(FormView): template_name = 'user/become-author.html' form_class = AuthorRequesForm success_url = reverse_lazy('author-request-success') def post(self, request, pk=None): form = self.get_form() if form.is_valid(): if request.user.is_authenticated: email = request.user.email if request.user.role in [User.AUTHOR_ROLE, User.ADMIN_ROLE]: messages.info(request, 'Вы уже являетесь автором') return self.form_invalid(form) else: email = form.cleaned_data['email'] if AuthorRequest.objects.filter(email=email).exists(): messages.error(request, 'Вы уже отправили заявку на преподавателя') return self.form_invalid(form) AuthorRequest.objects.create( first_name=form.cleaned_data['first_name'], last_name=form.cleaned_data['last_name'], email=email, about=form.cleaned_data['about'], facebook=form.cleaned_data['facebook'], ) return self.form_valid(form) else: return self.form_invalid(form) def get_context_data(self, **kwargs): if self.request.user.is_authenticated: self.initial = { 'first_name': self.request.user.first_name, 'last_name': self.request.user.last_name, 'email': self.request.user.email, 'about': self.request.user.about, 'facebook': self.request.user.facebook, } return super().get_context_data(**kwargs)