alkator/alkatorapi/views.py
Martin Quarda b5c8c005c6 small fix
2024-10-17 09:08:55 +02:00

635 lines
25 KiB
Python

from django.shortcuts import render
from django.http import HttpResponse
from django.template.response import TemplateResponse
from django.shortcuts import redirect
from django.views.decorators.csrf import csrf_exempt
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.auth.models import User as DjangoUser
from django.contrib.auth import authenticate
from django.contrib.auth import login as auth_login, logout as auth_logout
from django.core.mail import send_mail, mail_admins, EmailMessage
from django.db.utils import IntegrityError
from django.db import transaction
from django.utils.datastructures import MultiValueDictKeyError
from datetime import date, datetime, timedelta
from dateutil.relativedelta import relativedelta
from weasyprint import HTML
from urllib.parse import parse_qs
import secrets
import requests
import json
import glob
import PIL.Image
import random
import itertools
from collections import OrderedDict
from .models import (
User, ALKATOR_CHOICES_DICT, ALKATOR_CLASSES,
Profile, Racer, Invoice, Product, InvoiceProduct,
Cart, CartProduct
)
from alkator.settings import COMGATE_MERCHANT, COMGATE_SECRET, COMGATE_TEST
DEADLINE = date(2025, 10, 5)
ALKATOR_CLASS = 3
@csrf_exempt
def register_user(request):
if not request.POST['first_name']:
return HttpResponse('{"reason":"Jméno je povinné!"}', status=400, content_type='application/json')
if not request.POST['last_name']:
return HttpResponse('{"reason":"Přijmení je povinné!"}', status=400, content_type='application/json')
if not request.POST['email']:
return HttpResponse('{"reason":"Email je povinný!"}', status=400, content_type='application/json')
if not request.POST['address']:
return HttpResponse('{"reason":"Adresa je povinná!"}', status=400, content_type='application/json')
if not request.POST['password1'] or not request.POST['password2']:
return HttpResponse('{"reason":"Heslo je povinné!"}', status=400, content_type='application/json')
if request.POST['password1'] != request.POST['password2']:
return HttpResponse('{"reason":"Hesla se neshodují!"}', status=400, content_type='application/json')
if not request.POST['phone']:
return HttpResponse('{"reason":"Telefon je povinný"}', status=400, content_type='application/json')
if DjangoUser.objects.filter(email=request.POST['email']):
return HttpResponse('{"reason":"Email je již registrován!"}', status=400, content_type='application/json')
email = request.POST['email']
user = DjangoUser.objects.create_user(
email, email, request.POST['password1']
)
profile = Profile(
user=user,
first_name=request.POST['first_name'],
last_name=request.POST['last_name'],
address=request.POST['address'],
phone=request.POST['phone'],
)
profile.save()
auth_login(request, user)
return HttpResponse('{"success":"Úspěšná registrace!", "redirect":"/#"}', content_type='application/json')
@csrf_exempt
def forgotten_password(request):
if not request.POST['email']:
return HttpResponse('{"reason":"Email je povinný!"}', status=400, content_type='application/json')
if not request.POST['password1'] or not request.POST['password2']:
return HttpResponse('{"reason":"Heslo je povinné!"}', status=400, content_type='application/json')
if request.POST['password1'] != request.POST['password2']:
return HttpResponse('{"reason":"Hesla se neshodují!"}', status=400, content_type='application/json')
if not request.POST['code']:
return HttpResponse('{"reason":"Kód pro obnovení hesla je povinný!"}', status=400, content_type='application/json')
try:
user = DjangoUser.objects.get(username=request.POST['email'])
except DjangoUser.DoesNotExist:
return HttpResponse('{"reason":"Účet nenalezen!"}', status=404, content_type='application/json')
if user.profile.forgotten_password_code != request.POST['code']:
return HttpResponse('{"reason":"Špatný kód!"}', status=400, content_type='application/json')
user.set_password(request.POST['password1'])
user.save()
auth_login(request, user)
return HttpResponse('{"success":"Úspěšně změněné heslo uživatele ' + user.email + '!", "redirect":"/#"}', content_type='application/json')
@csrf_exempt
def login(request):
if "forgotten_password" in request.POST:
email = request.POST["email"]
try:
user = DjangoUser.objects.get(username=email)
except DjangoUser.DoesNotExist:
return HttpResponse('{"reason":"Nezadané jméno nebo uživatel neexistuje!"}', status=404, content_type='application/json')
code = secrets.token_urlsafe(10)
user.profile.forgotten_password_code = code
user.profile.save()
mail = EmailMessage("zapomenuté heslo v Alkátor Race", f"""Zdravím tě Alkátore,
kód pro změnu hesla: {code}
Změna hesla probíhá na stránce: https://alkator.cz/#forgotten_password
Na tento email není třeba odpovídat, protože je generován automaticky. V případě potřeby pište na info@alkator.cz .
ALKÁTOR TEAM
email: info@alkator.cz
tel: + 420 728 018 088
web: https://alkator.cz""", "info@alkator.cz", [request.POST["email"]])
mail.send()
return HttpResponse('{"success":"Úspěšně poslán kód pro obnovení hesla uživatele '+ user.email + '", "redirect":"/#forgotten_password"}', content_type='application/json')
else:
try:
user = authenticate(request, username=request.POST['email'], password=request.POST['password'])
except MultiValueDictKeyError:
return HttpResponse('{"reason":"Nezadané jméno nebo heslo!"}', status=400, content_type='application/json')
if user is not None:
auth_login(request, user)
return HttpResponse('{"success":"Úspěšně přihlášen uživatel '+ user.email + '", "redirect":"/#"}', content_type='application/json')
else:
return HttpResponse('{"reason":"Nesprávné jméno nebo heslo!"}', status=400, content_type='application/json')
@csrf_exempt
def logout(request):
auth_logout(request)
return redirect("/#")
@csrf_exempt
def register_racer(request):
if not request.user.is_authenticated:
return HttpResponse('{"reason":"Je potřeba se přihlásit!"}', status=400, content_type='application/json')
if date.today() >= DEADLINE:
return HttpResponse('{"reason":"Too late!"}', status=400, content_type='application/json')
if not request.POST.get('agreement'):
return HttpResponse('{"reason":"Je potřeba souhlasit se zpracováním údajů!"}', status=400, content_type='application/json')
if not request.POST['first_name']:
return HttpResponse('{"reason":"Jméno je povinné!"}', status=400, content_type='application/json')
if not request.POST['last_name']:
return HttpResponse('{"reason":"Přijmení je povinné!"}', status=400, content_type='application/json')
try:
dob = datetime.strptime(request.POST['date_of_birth'], "%Y-%m-%d").date()
if dob > DEADLINE - relativedelta(years=18):
return HttpResponse('{"reason":"Je potřeba mít 18 let v den závodu!"}', status=400, content_type='application/json')
elif dob < DEADLINE - relativedelta(years=100):
return HttpResponse('{"reason":"Opravdu vám je 100 let?"}', status=400, content_type='application/json')
except:
return HttpResponse('{"reason":"Špatný formát datu narození!"}', status=400, content_type='application/json')
invoice_id = Invoice.next_invoice_id()
profile = request.user.profile
user = request.user
product = Product.objects.get(id=1)
price = product.price
if product.quantity <= 0:
return HttpResponse('{"reason":"Jsme vyprodaní!"}', status=400, content_type='application/json')
invoice = Invoice(
invoice_id=invoice_id,
user=user,
total_price=price,
address="",
)
racer = Racer(
product=product,
invoice=invoice,
quantity=1,
profile = profile,
first_name = request.POST['first_name'],
last_name = request.POST['last_name'],
email = request.POST['email'],
team = request.POST['team'],
phone = request.POST['phone'],
date_of_birth = dob,
alkator_class = ALKATOR_CLASS,
price=price,
)
invoice.save()
racer.save()
payment_data = {
'merchant': COMGATE_MERCHANT,
'test': 'true' if COMGATE_TEST else 'false',
'price': price * 100,
'curr': 'CZK',
'method': 'ALL',
'label': 'Startovné na závod Alkátor Race Dolní Čermná 2025',
'email': user.email,
'fullName': f"{profile.first_name} {profile.last_name}",
'refId': f'{invoice.invoice_id}',
'secret': COMGATE_SECRET,
'prepareOnly': 'true',
}
result = requests.post('https://payments.comgate.cz/v1.0/create', data=payment_data)
result = parse_qs(result.text)
if result['code'][0] != '0':
racer.delete()
invoice.delete()
return HttpResponse('{"reason":"Chyba na straně platební brány: ' + result['message'][0] + ', zkuste prosím registraci později."}', status=400, content_type='application/json')
product.quantity -= 1
product.save()
invoice.trans_id = result['transId'][0]
invoice.save()
return HttpResponse('{"success":"", "redirect":"' + result['redirect'][0] + '"}', content_type='application/json')
def login_status(request):
if not request.user.is_authenticated:
return HttpResponse('{}', content_type='application/json')
user = request.user
racers = user.profile.racers.all()
return HttpResponse(json.dumps({
"email": user.email,
"first_name": user.profile.first_name,
"last_name": user.profile.last_name,
"address": user.profile.address,
"racers": [{
"id": racer.invoice_id,
"first_name": racer.first_name,
"last_name": racer.last_name,
"email": racer.email,
"phone": racer.phone,
"team": racer.team,
"date_of_birth": racer.date_of_birth.strftime("%Y-%m-%d"),
"paid": racer.invoice.paid,
} for racer in racers]
}), content_type='application/json')
@csrf_exempt
def change_racer(request):
try:
racer = Racer.objects.get(invoice_id=request.POST['id'])
if request.user != racer.profile.user:
return HttpResponse('{"reason":"Nedostatečná práva!"}', status=400, content_type='application/json')
if date.today() >= DEADLINE:
return HttpResponse('{"reason":"Příliš pozdě na změnu účastníka!"}', status=400, content_type='application/json')
if not request.POST['first_name']:
return HttpResponse('{"reason":"Jméno je povinné!"}', status=400, content_type='application/json')
if not request.POST['last_name']:
return HttpResponse('{"reason":"Přijmení je povinné!"}', status=400, content_type='application/json')
try:
dob = datetime.strptime(request.POST['date_of_birth'], "%Y-%m-%d").date()
if dob > DEADLINE - relativedelta(years=18):
return HttpResponse('{"reason":"Je potřeba mít 18 let v den závodu!"}', status=400, content_type='application/json')
elif dob < DEADLINE - relativedelta(years=100):
return HttpResponse('{"reason":"Opravdu vám je 100 let?"}', status=400, content_type='application/json')
except:
return HttpResponse('{"reason":"Špatný formát datu narození!"}', status=400, content_type='application/json')
racer.first_name = request.POST['first_name']
racer.last_name = request.POST['last_name']
racer.email = request.POST['email']
racer.phone = request.POST['phone']
racer.team = request.POST['team']
racer.date_of_birth = dob
racer.save()
return HttpResponse('{"success":"Úspěšně uloženo."}', content_type='application/json')
except MultiValueDictKeyError:
return HttpResponse('{"reason":"Nějaký údaj chybí!"}', status=400, content_type='application/json')
except Racer.DoesNotExist:
return HttpResponse('{"reason":"Upravujete neexistujícího závodníka!"}', status=400, content_type='application/json')
@csrf_exempt
def payment_result(request):
result = parse_qs(request.body.decode('utf8'))
ref_id = int(result['refId'][0])
paid = result['status'][0]
secret_match = result['secret'][0] == COMGATE_SECRET
test = result['test'][0] != 'false'
if not secret_match or test != COMGATE_TEST:
return HttpResponse(status=400)
try:
invoice = Invoice.objects.get(invoice_id=ref_id)
user = invoice.user
except Invoice.DoesNotExist:
mail_admins('Chyba s platbou!', f'invoice_id={ref_id}&paid={paid}')
return HttpResponse(status=404)
if paid == 'PAID':
invoice.paid = True
invoice.save()
try:
racer = Racer.objects.get(invoice=invoice)
mail = EmailMessage(
subject=f"úspěšná registrace do závodu Alkátor race Dolní Čermná ({racer.first_name} {racer.last_name})",
body=f"""Zdravím tě Alkátore,
toto je potvrzovací email o účasti v nezapomenutelném závodě Alkátor-race.
Prosíme kontrolujete si své hromadné schránky a spamy.
Závod se koná 5.10.2024 v Dolní Čermné.
Zajištěno je stanování, občerstvení a také večerní after párty.
Připrav na zkoušku své tělo, játra a tvé mozkové buňky.
Budou tě čekat nevídané překážky.
O podrobnostech tě budeme informovat emailem.
Nezapomeň nás sledovat na Facebooku, Instagramu, YouTube a TikToku.
Na tento email není třeba odpovídat, protože je generován automaticky s přijatou platbou. V případě potřeby pište na info@alkator.cz .
ALKÁTOR TEAM
email: info@alkator.cz
tel: + 420 728 018 088
web: https://alkator.cz""",
from_email="info@alkator.cz",
to=[invoice.user.email, racer.email] if racer.email and invoice.user.email != racer.email else [invoice.user.email],
bcc=["info@alkator.cz"],
cc=[]
)
except Racer.DoesNotExist:
mail = EmailMessage(
subject=f"úspěšně zaplacený nákup v Alkátor shopu",
body=f"""Zdravím tě Alkátore,
úspěšně si zakoupil merch na Alkátor shopu.
Na tento email není třeba odpovídat, protože je generován automaticky s přijatou platbou. V případě potřeby pište na info@alkator.cz .
ALKÁTOR TEAM
email: info@alkator.cz
tel: + 420 728 018 088
web: https://alkator.cz""",
from_email="info@alkator.cz",
to=[invoice.user.email],
bcc=["info@alkator.cz"],
cc=[]
)
template = TemplateResponse(
None,
'invoice.html',
{
'user': user,
'invoice': invoice,
'products': InvoiceProduct.objects.filter(invoice=invoice),
'paid_date': invoice.paid_date + timedelta(days=1),
}
)
template.render()
pdf_name = f"invoices/{invoice.invoice_id}_{user.profile.last_name}_{user.profile.first_name}.pdf"
HTML(string=template.content).write_pdf(pdf_name)
attach = open(pdf_name, 'rb')
mail.attach('faktura.pdf', attach.read(), 'application/pdf')
if not mail.send():
return HttpResponse(status=500)
elif paid == 'CANCELLED' and not invoice.paid:
for ip in InvoiceProduct.objects.filter(invoice=invoice):
ip.product.quantity += ip.quantity
ip.product.save()
ip.delete()
invoice.delete()
return HttpResponse(status=200)
def payment_state(request):
invoice_id = request.GET['refId']
try:
if Invoice.objects.get(invoice_id=invoice_id).paid:
return HttpResponse('{"status":"success", "reason":"Úspěšná platba"}', content_type='application/json')
else:
return HttpResponse('{"status":"failed", "reason":"Zatím nemáme informace o provedené platbě. Zkuste reload nebo zkontrolujte email."}', content_type='application/json')
except Invoice.DoesNotExist:
return HttpResponse('{"status":"failed", "reason":"Závodník neexistuje, registraci závodníka prosím opakujte."}', content_type='application/json')
def products(request):
return HttpResponse(json.dumps([
{
'id': product.id,
'name': product.name,
'description': product.description,
'img': product.img.url,
'price': product.price,
'quantity': product.quantity,
}
for product in Product.objects.all() if product.quantity > 0 and not product.hidden
]), content_type='application/json')
def cart_add(request):
_id = request.GET['id']
user = request.user
if not user.is_authenticated:
return HttpResponse('{"status": "failed", "reason": "Pro využivání Košíku je třeba se přihlásit!"}')
try:
cart = user.cart
except AttributeError:
cart = Cart(user=user)
cart.save()
product = Product.objects.get(id=_id)
try:
cart_product = CartProduct(
product=product,
cart=cart,
quantity=1,
)
cart_product.save()
except IntegrityError as e:
cart_product = CartProduct.objects.get(cart=cart, product=product)
cart_product.quantity += 1
if cart_product.quantity > product.quantity:
return HttpResponse('{"status": "failed", "reason": "Nemáme dostatek předmětů na skladě!"}')
cart_product.save()
return HttpResponse('{"status":"success", "reason":"Úspěšně přidáno do košíku."}', status=200)
def select_delivery(request):
user = request.user
cart = user.cart
cart.address = request.GET['delivery']
cart.save()
return HttpResponse('{"status":"success", "reason":"Úspěšně vybraná zásilkovna."}', status=200)
def delivery(request):
try:
if request.user.cart.address:
return HttpResponse('"' + request.user.cart.address + '"', status=200, content_type='application/json')
return HttpResponse("undefined", status=200, content_type='application/json')
except AttributeError:
return HttpResponse("undefined", status=200, content_type='application/json')
@csrf_exempt
@transaction.atomic
def cart_buy(request):
cart = request.user.cart
user = request.user
profile = user.profile
cart_products = CartProduct.objects.filter(cart=cart)
for cp in cart_products:
if cp.quantity > cp.product.quantity:
return HttpResponse('{"status": "failed", "reason": "Nemáme dostatek ' + cp.product.name + ' na skladě!"}', status=400, content_type='application/json')
if not cart.address:
return HttpResponse('{"status": "failed", "reason": "Je potřeba vybrat výběrní místo!"}', status=400, content_type='application/json')
invoice = Invoice(
user=user,
address=cart.address,
invoice_id=Invoice.next_invoice_id(),
total_price=0,
)
total_price = 0
invoice.save()
for cp in itertools.chain(cart_products, [CartProduct(quantity=1, product=Product.objects.get(id=2), cart=cart)]):
ip = InvoiceProduct(
invoice=invoice,
price=cp.product.price,
quantity=cp.quantity,
product=cp.product,
)
cp.product.quantity -= cp.quantity
cp.product.save()
total_price += cp.quantity * cp.product.price
ip.save()
if total_price <= 79:
return HttpResponse('{"status": "failed", "reason": "Prosím přidejte svoje položky do košíku!"}', status=400, content_type='application/json')
payment_data = {
'merchant': COMGATE_MERCHANT,
'test': 'true' if COMGATE_TEST else 'false',
'price': total_price * 100,
'curr': 'CZK',
'method': 'ALL',
'label': 'Merch Alkátor Race',
'email': user.email,
'fullName': f"{profile.first_name} {profile.last_name}",
'refId': f'{invoice.invoice_id}',
'secret': COMGATE_SECRET,
'prepareOnly': 'true',
}
result = requests.post('https://payments.comgate.cz/v1.0/create', data=payment_data)
result = parse_qs(result.text)
if result['code'][0] != '0':
for ip in InvoiceProduct.objects.filter(invoice=invoice):
ip.delete()
invoice.delete()
return HttpResponse('{"reason":"Chyba na straně platební brány: ' + result['message'][0] + ', zkuste prosím nákup později."}', status=400, content_type='application/json')
invoice.total_price = total_price
invoice.trans_id = result['transId'][0]
cart.delete()
invoice.save()
return HttpResponse('{"success":"", "redirect":"' + result['redirect'][0] + '"}', content_type='application/json')
def cart_delete(request):
_id = request.GET['id']
user = request.user
cart = user.cart
product = Product.objects.get(id=_id)
cart_product = CartProduct.objects.get(cart=cart, product=product)
cart_product.delete()
return HttpResponse('{"status":"success", "reason":"Úspěšně odstraněno z košíku."}', content_type='application/json')
def cart_decrease(request):
_id = request.GET['id']
user = request.user
cart = user.cart
product = Product.objects.get(id=_id)
cart_product = CartProduct.objects.get(cart=cart, product=product)
cart_product.quantity -= 1
if cart_product.quantity == 0:
cart_product.delete()
else:
cart_product.save()
return HttpResponse('{"status":"success", "reason":"Úspěšně sníženo množství v košíku."}', content_type='application/json')
def cart(request):
user = request.user
if not user.is_authenticated:
return HttpResponse("[]", content_type='application/json')
cart = []
try:
for cart_product in CartProduct.objects.filter(cart=user.cart):
cart.append({
'id': cart_product.product.id,
'name': cart_product.product.name,
'description': cart_product.product.description,
'quantity': cart_product.quantity,
'price': cart_product.product.price,
'img': cart_product.product.img.url,
})
except AttributeError:
return HttpResponse("[]", content_type='application/json')
return HttpResponse(json.dumps(cart))
def results(request):
results = []
n = 1
for user in User.objects.filter(alkator_class=2).order_by('duration'):
if user.duration:
if user.alkator_category == 1:
order = f'{n}.'
n += 1
else:
order = 'x.'
results.append({
'order': order,
'duration': str(user.duration),
'alkator_category': ALKATOR_CHOICES_DICT[user.alkator_category],
'starting_number': user.starting_number,
})
for user in User.objects.filter(alkator_class=2).order_by('duration'):
if not user.duration and user.starting_number:
results.append({
'order': 'DNF',
'duration': 'Nedokončil',
'starting_number': user.starting_number,
'alkator_category': ''
})
return HttpResponse(json.dumps(results), content_type='application/json')
def photos(request):
rtn = []
for (i, name) in ALKATOR_CLASSES[:2]:
photos = {}
rtn.append(photos)
photos['name'] = name
photos['index'] = i
photos['photos'] = []
files = glob.glob(f"photos/{i}/*.jpg")
random.shuffle(files)
for file in files:
img = PIL.Image.open(file)
photos['photos'].append({
'original': '/' + file.replace(".jpg", ".webp"),
'thumbnail': '/' + file.replace(f'photos/{i}/', f'photos/{i}/thumbnail/').replace(".jpg", ".webp"),
'original_width': img.width,
'original_height': img.height,
})
return HttpResponse(json.dumps(rtn), content_type='application/json')
@csrf_exempt
def upload_files(request):
for file in request.FILES.getlist('files'):
rand = random.randint(0, 99999)
random_name = f'photos/uploads/{rand}_{file.name}'
with open(random_name, "wb+") as destination:
for chunk in file.chunks():
destination.write(chunk)
return HttpResponse('{"success":"Úspěšně nahráno! Počkejte až dojde ke schválení nahraných souborů."}', content_type='application/json')
@staff_member_required
def invoice(request):
invoice = Invoice.objects.get(invoice_id=request.GET['invoice_id'])
user = DjangoUser.objects.get(id=invoice.user_id)
return TemplateResponse(
None,
'invoice.html',
{
'user': user,
'invoice': invoice,
'products': InvoiceProduct.objects.filter(invoice=invoice),
'paid_date': invoice.paid_date + timedelta(days=1),
}
)