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.
 
 
 
 
 
 

398 lines
17 KiB

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
import short_url
from django.conf import settings
from django.contrib.auth import login
from django.shortcuts import 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
from django.db.models import F, Func, Sum, Min, Max, Q
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.config.models import Config
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, Payment, UserGiftCertificate, UserBonus, \
DrawingCampPayment
from apps.user.models import AuthorRequest, EmailSubscription, SubscriptionCategory, Child
from .forms import AuthorRequesForm, UserEditForm, WithdrawalForm, ChildForm
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['user_gift_certificates'] = UserGiftCertificate.objects.filter(user=self.request.user)
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_end__gte=now() - timedelta(7),
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')]
camp_payment = DrawingCampPayment.objects.filter(
user=self.object,
date_end__gte=now() - timedelta(7),
status__in=DrawingCampPayment.PW_PAID_STATUSES,
)
context['is_camp_purchased'] = camp_payment.exists()
if context['is_camp_purchased']:
aggregated = camp_payment.aggregate(Sum('amount'), Min('date_start'), Max('date_end'),)
context['camp_purchased_price'] = aggregated.get('amount__sum') or 0
context['camp_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')
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')
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 get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
if self.object.childs.all().count():
context['childs'] = [{
'id': c.id,
'first_name': c.first_name,
'last_name': c.last_name,
'gender': c.gender or 'n',
'birthday': c.birthday.strftime('%Y-%m-%d') if c.birthday else '',
} for c in self.object.childs.all()]
else:
context['childs'] = [{
'id': '',
'first_name': '',
'last_name': '',
'gender': 'n',
'birthday': '',
}]
return self.render_to_response(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, 'Неверный пароль.')
child_ids = request.POST.getlist('child_id')
child_first_names = request.POST.getlist('child_first_name')
child_last_names = request.POST.getlist('child_last_name')
child_genders = request.POST.getlist('child_gender')
child_birthdays = request.POST.getlist('child_birthday')
ziped = zip(child_ids, child_first_names, child_last_names, child_genders, child_birthdays)
childs = [dict(zip(['id', 'first_name', 'last_name', 'gender', 'birthday'], z)) for z in ziped]
childs_saved = []
child_errors = False
for child in childs:
child_instance = None
if child.get('id'):
child_instance = Child.objects.get(pk=child.get('id'))
child['user'] = request.user.id
child['gender'] = child['gender'] or 'n'
child_form = ChildForm(data=child, instance=child_instance)
if child_form.is_valid():
childs_saved.append(child_form.save().id)
elif not(len(childs) == 1 and not child.get('id') and not child.get('first_name')):
child_errors = True
child['errors'] = {f: e[0] for f, e in child_form.errors.items()}
if not child_errors:
request.user.childs.exclude(id__in=childs_saved).delete()
form = self.get_form()
if form.is_valid() and not child_errors:
messages.info(request, 'Данные сохранены.')
response = self.form_valid(form)
# начисляем бонусы, если заполнил профиль
if self.object.phone and self.object.first_name and self.object.last_name and not self.object.bonuses.filter(
is_service=True, action_name=UserBonus.ACTION_FILL_PROFILE).count():
UserBonus.objects.create(user=self.object, amount=UserBonus.AMOUNT_FILL_PROFILE, is_service=True,
action_name=UserBonus.ACTION_FILL_PROFILE)
return response
else:
context = self.get_context_data(**kwargs)
context['childs'] = childs
return self.render_to_response(context)
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)
@method_decorator(login_required, name='dispatch')
class BonusHistoryView(TemplateView):
model = User
template_name = 'user/bonus-history.html'
def get(self, request, *args, **kwargs):
config = Config.load()
context = self.get_context_data(**kwargs)
context['bonuses'] = request.user.bonuses.filter(
Q(payment__isnull=False, payment__status__in=Payment.PW_PAID_STATUSES) | Q(is_service=True),
)
context['referrer_url'] = 'https://%s%s?referrer=%s' % (
settings.MAIN_HOST, reverse('index'), short_url.encode_url(request.user.id)
)
context['share_text'] = '%s приглашает вас и ваших детей в первую онлайн школу креативного мышления Lil School. ' \
'Занимайтесь с ребёнком творчеством, не выходя из дома. ' \
'Перейдите по ссылке и получите скидку %d%% на первую покупку' \
% (request.user.get_full_name(), config.REFERRAL_BONUS)
context.update({
'amount_have_review': UserBonus.AMOUNT_HAVE_REVIEW,
'amount_fill_profile': UserBonus.AMOUNT_FILL_PROFILE,
'amount_paid_one_more': UserBonus.AMOUNT_PAID_ONE_MORE,
'amount_child_birthday': UserBonus.AMOUNT_CHILD_BIRTHDAY,
})
return self.render_to_response(context)
@method_decorator(login_required, name='dispatch')
class UserGalleryEditView(TemplateView):
template_name = 'user/edit-gallery.html'