views.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. from django.shortcuts import render, get_object_or_404, redirect
  2. from django.contrib.auth.decorators import login_required, user_passes_test
  3. from django.contrib import messages
  4. from django.contrib.admin.views.decorators import staff_member_required
  5. from django.core.mail import EmailMultiAlternatives
  6. from django.conf import settings
  7. from django.template.loader import render_to_string
  8. from django.utils import timezone
  9. from datetime import date
  10. import logging
  11. from django.core.mail import send_mass_mail
  12. from django.core.mail import BadHeaderError
  13. from smtplib import SMTPException
  14. from django.http import HttpResponse
  15. from rest_framework.response import Response
  16. from rest_framework.decorators import api_view
  17. from .serializers import EventoSerializer, ReservaSerializer, ListaEsperaSerializer, NoticiaSerializer
  18. from .models import Evento, Reserva, ListaEspera, Noticia
  19. from .forms import ListaEsperaForm, EventoForm, MensajeCorreoForm, ReservaForm
  20. from django.contrib.auth import get_user_model
  21. from django.db.models import Count, Q
  22. User = get_user_model()
  23. logger = logging.getLogger(__name__)
  24. # helper for sending reservation emails
  25. def enviar_correo_reserva(evento, usuario, acompanante=None):
  26. """Envía los correos de confirmación al usuario (y acompañante) y notificación al admin.
  27. Parameters
  28. ----------
  29. evento : Evento
  30. Instancia del evento reservado.
  31. usuario : User
  32. Usuario que realiza la reserva.
  33. acompanante : User | None
  34. Si se trata de un evento para parejas, usuario acompañante opcional.
  35. """
  36. from_email = settings.DEFAULT_FROM_EMAIL
  37. to_email = usuario.email
  38. cc_email = []
  39. # cc siempre al administrador; al acompañante si existe
  40. # cc_email = [settings.ADMIN_EMAIL]
  41. if acompanante and acompanante.email:
  42. cc_email.append(acompanante.email)
  43. # cuerpo de texto plano
  44. texto = f'Hola {usuario.nombre}, \n\n'
  45. texto += f'Te has inscrito correctamente en el evento "{evento.nombre}".\n'
  46. texto += f'Detalles del evento:\n'
  47. texto += f'Nombre: {evento.nombre}\n'
  48. texto += f'Descripción: {evento.descripcion}\n'
  49. texto += f'Fecha: {evento.fecha.strftime("%d/%b/%y")} a las {evento.hora}\n'
  50. texto += f'Precio: {evento.precio} €'
  51. if acompanante:
  52. texto += f"\nHas indicado como pareja a {acompanante.nombre}.\n"
  53. texto += '\n¡Gracias por inscribirte!\n\n'
  54. texto += '\n\nIMPORTANTE: Para gestionar cambios o bajas NO contestes a este correo, escribe al administrador por WhatsApp\n\n'
  55. # plantilla HTML adecuada
  56. if acompanante:
  57. html_content = render_to_string(
  58. 'emails/confirmacion_reserva_parejas.html',
  59. {'evento': evento, 'usuario': usuario, 'acompanante': acompanante}
  60. )
  61. else:
  62. html_content = render_to_string(
  63. 'emails/confirmacion_reserva.html',
  64. {'evento': evento, 'usuario': usuario}
  65. )
  66. msg = EmailMultiAlternatives(
  67. f'Confirmación de Reserva para {evento.nombre}',
  68. texto,
  69. from_email,
  70. [to_email],
  71. cc=cc_email
  72. )
  73. msg.attach_alternative(html_content, "text/html")
  74. msg.send()
  75. # notificación al administrador (se envía siempre incluso si ya está
  76. # en cc porque el formato es distinto y queremos consolidar datos de plazas)
  77. asunto_admin = f'[{evento.plazas_restantes()}/{evento.plazas_disponibles}] Nueva Reserva para {evento.nombre}'
  78. mensaje_admin = (
  79. f'El usuario {usuario.nombre} ({usuario.email}) se ha inscrito en el evento "{evento.nombre}".\n'
  80. f'Quedan {evento.plazas_restantes()} plazas disponibles.'
  81. )
  82. html_content_admin = render_to_string(
  83. 'emails/notificacion_reserva_admin.html',
  84. {
  85. 'evento': evento,
  86. 'usuario': usuario,
  87. 'acompanante': acompanante,
  88. 'plazas_restantes': evento.plazas_restantes(),
  89. 'plazas_disponibles': evento.plazas_disponibles,
  90. },
  91. )
  92. email_admin = EmailMultiAlternatives(
  93. asunto_admin,
  94. mensaje_admin,
  95. settings.DEFAULT_FROM_EMAIL,
  96. [settings.ADMIN_EMAIL],
  97. )
  98. email_admin.attach_alternative(html_content_admin, "text/html")
  99. email_admin.send()
  100. def enviar_correo_lista_espera(evento, usuario, acompanante=None):
  101. """Envía correos cuando un usuario se apunta a la lista de espera.
  102. Parameters
  103. ----------
  104. evento : Evento
  105. usuario : User
  106. acompanante : User | None
  107. """
  108. from_email = settings.DEFAULT_FROM_EMAIL
  109. to_email = usuario.email
  110. cc_email = []
  111. if acompanante and acompanante.email:
  112. cc_email.append(acompanante.email)
  113. texto = f'Hola {usuario.nombre},\n\n'
  114. texto += f'Te has añadido a la lista de espera del evento "{evento.nombre}".\n'
  115. texto += f'Detalles del evento:\n'
  116. texto += f'Nombre: {evento.nombre}\n'
  117. texto += f'Descripción: {evento.descripcion}\n'
  118. texto += f'Fecha: {evento.fecha.strftime("%d/%b/%y")} a las {evento.hora}\n'
  119. if acompanante:
  120. texto += f"\nHas indicado como pareja a {acompanante.nombre}.\n"
  121. texto += '\nTe avisaremos si se libera alguna plaza.\n\n'
  122. texto += '\n\nIMPORTANTE: Para gestionar cambios o bajas NO contestes a este correo, escribe al administrador por WhatsApp\n\n'
  123. if acompanante:
  124. html = render_to_string(
  125. 'emails/confirmacion_lista_espera_parejas.html',
  126. {'evento': evento, 'usuario': usuario, 'acompanante': acompanante},
  127. )
  128. else:
  129. html = render_to_string(
  130. 'emails/confirmacion_lista_espera.html',
  131. {'evento': evento, 'usuario': usuario},
  132. )
  133. subject = f'Confirmación lista de espera para {evento.nombre}'
  134. msg = EmailMultiAlternatives(subject, texto, from_email, [to_email], cc=cc_email)
  135. msg.attach_alternative(html, 'text/html')
  136. msg.send()
  137. # notificar al admin
  138. asunto_admin = f'Nueva lista de espera para {evento.nombre}'
  139. mensaje_admin = (
  140. f'El usuario {usuario.nombre} ({usuario.email}) se ha añadido a la lista de espera.'
  141. )
  142. if acompanante:
  143. mensaje_admin += f' Acompañante: {acompanante.nombre} ({acompanante.email}).'
  144. html_admin = render_to_string(
  145. 'emails/notificacion_lista_espera_admin.html',
  146. {'evento': evento, 'usuario': usuario, 'acompanante': acompanante},
  147. )
  148. email_admin2 = EmailMultiAlternatives(
  149. asunto_admin,
  150. mensaje_admin,
  151. settings.DEFAULT_FROM_EMAIL,
  152. [settings.ADMIN_EMAIL],
  153. )
  154. email_admin2.attach_alternative(html_admin, 'text/html')
  155. email_admin2.send()
  156. @login_required
  157. def reservar_evento(request, evento_id):
  158. evento = get_object_or_404(Evento, id=evento_id)
  159. # Verificar si el usuario ya tiene una reserva para este evento
  160. if Reserva.objects.filter(evento=evento, usuario=request.user).exists():
  161. messages.error(request, 'Ya tienes una reserva para este evento.')
  162. return redirect('eventos:lista_eventos')
  163. # Verificar si quedan plazas disponibles
  164. if evento.plazas_restantes() > 0:
  165. Reserva.objects.create(evento=evento, usuario=request.user, iniciador=True)
  166. # separar el envío de correos a función reutilizable
  167. enviar_correo_reserva(evento, request.user)
  168. messages.success(request,
  169. f'Reserva realizada para el evento {evento.nombre}.')
  170. else:
  171. messages.error(request, 'Lo sentimos, no quedan plazas disponibles.')
  172. return redirect('eventos:lista_eventos')
  173. @login_required
  174. def reservar_evento_parejas(request, evento_id):
  175. evento = get_object_or_404(Evento, id=evento_id)
  176. # Si ya está inscrito
  177. if Reserva.objects.filter(evento=evento, usuario=request.user).exists():
  178. messages.warning(request, "Ya estás inscrito en este evento.")
  179. return redirect('eventos:detalle_evento', evento_id=evento.id)
  180. if request.method == 'POST':
  181. form = ReservaForm(request.POST, evento=evento, usuario=request.user)
  182. if form.is_valid():
  183. acompanante = form.cleaned_data.get('acompanante')
  184. # Crear la reserva principal
  185. Reserva.objects.create(
  186. evento=evento,
  187. usuario=request.user,
  188. iniciador=True,
  189. acompanante=acompanante if evento.para_parejas else None
  190. )
  191. # Si es un evento para parejas, crear también la reserva del acompañante
  192. if evento.para_parejas and acompanante:
  193. # Solo si el acompañante no está ya inscrito
  194. if not Reserva.objects.filter(evento=evento, usuario=acompanante).exists():
  195. Reserva.objects.create(
  196. evento=evento,
  197. usuario=acompanante,
  198. iniciador=False,
  199. acompanante=request.user
  200. )
  201. # Enviar correos de confirmación/aviso usando la función reutilizable
  202. enviar_correo_reserva(evento, request.user, acompanante=acompanante if evento.para_parejas else None)
  203. messages.success(request, "Reserva realizada correctamente.")
  204. return redirect('eventos:detalle_evento', evento_id=evento.id)
  205. else:
  206. form = ReservaForm(evento=evento, usuario=request.user)
  207. return render(request, 'eventos/reservar_evento.html', {'form': form, 'evento': evento})
  208. @user_passes_test(lambda u: u.is_staff)
  209. def publicar_evento(request, evento_id):
  210. evento = get_object_or_404(Evento, id=evento_id)
  211. if not evento.publicado:
  212. evento.publicado = True
  213. evento.save()
  214. messages.success(request, 'El evento %s ha sido publicado' % evento.nombre)
  215. else:
  216. messages.info(request, 'El evento %s ya está publicado' % evento.nombre)
  217. return redirect('eventos:detalle_evento', evento_id=evento_id)
  218. @login_required
  219. def lista_eventos(request):
  220. # anno_actual = timezone.now().year
  221. # Solo mostrar los eventos publicados a los usuarios normales
  222. if request.user.is_staff:
  223. eventos = Evento.objects.all().order_by('-fecha', '-hora')
  224. else:
  225. eventos = Evento.objects.filter(publicado=True).order_by('-fecha', '-hora')
  226. # eventos = Evento.objects.filter(publicado=True, fecha__year=anno_actual) # Los usuarios ven solo eventos publicados y del año en curso
  227. # Crear un diccionario que mapea cada evento con un booleano indicando si el usuario ya ha reservado
  228. eventos_con_reserva = []
  229. for evento in eventos:
  230. inscrito = Reserva.objects.filter(evento=evento, usuario=request.user).exists() if request.user.is_authenticated else False
  231. if ListaEspera.objects.filter(evento=evento, usuario=request.user).exists():
  232. en_espera = True
  233. else:
  234. en_espera = False
  235. eventos_con_reserva.append({
  236. 'evento': evento,
  237. 'inscrito': inscrito,
  238. 'id': evento.id,
  239. 'nombre': evento.nombre,
  240. 'fecha': evento.fecha,
  241. 'hora': evento.hora,
  242. 'plazas_disponibles': evento.plazas_disponibles,
  243. 'plazas_restantes': evento.plazas_restantes,
  244. 'en_espera': en_espera,
  245. 'usuario': request.user,
  246. 'publicado': evento.publicado,
  247. 'url_imagen': evento.url_imagen,
  248. 'para_parejas': evento.para_parejas
  249. })
  250. return render(request, 'eventos/lista_eventos.html', {'eventos_con_reserva': eventos_con_reserva})
  251. def detalle_noticia(request, noticia_id):
  252. noticia = get_object_or_404(Noticia, id=noticia_id, publicado=True)
  253. return render(request, 'detalle_noticia.html', {'noticia': noticia})
  254. def ayuda(request):
  255. return render(request, 'eventos/help.html')
  256. @login_required
  257. def detalle_evento(request, evento_id):
  258. el_evento = []
  259. evento = get_object_or_404(Evento, pk=evento_id)
  260. reservas = Reserva.objects.filter(evento=evento_id)
  261. inscrito = Reserva.objects.filter(evento=evento, usuario=request.user).exists() if request.user.is_authenticated else False
  262. if ListaEspera.objects.filter(evento=evento, usuario=request.user).exists():
  263. en_espera = True
  264. else:
  265. en_espera = False
  266. lista_espera = ListaEspera.objects.filter(evento=evento_id)
  267. el_evento.append(
  268. {
  269. 'evento': evento,
  270. 'inscrito': inscrito,
  271. 'id': evento.id,
  272. 'nombre': evento.nombre,
  273. 'descripcion': evento.descripcion,
  274. 'fecha': evento.fecha,
  275. 'hora': evento.hora,
  276. 'precio': evento.precio,
  277. 'plazas_disponibles': evento.plazas_disponibles,
  278. 'plazas_restantes': evento.plazas_restantes,
  279. 'en_espera': en_espera,
  280. 'usuario': request.user,
  281. 'publicado': evento.publicado,
  282. 'url_imagen': evento.url_imagen,
  283. 'para_parejas': evento.para_parejas,
  284. 'lugar': evento.lugar
  285. }
  286. )
  287. return render(request, 'eventos/detalle_evento.html', {'eventos': el_evento, 'reservas': reservas, 'lista': lista_espera})
  288. # Verificar si el usuario es administrador
  289. def es_admin(user):
  290. return user.is_staff
  291. @user_passes_test(es_admin)
  292. def crear_evento(request):
  293. if request.method == 'POST':
  294. form = EventoForm(request.POST)
  295. if form.is_valid():
  296. form.save()
  297. messages.success(request, 'Evento creado exitosamente.')
  298. return redirect('eventos:lista_eventos') # Redirige a la lista de eventos
  299. else:
  300. form = EventoForm()
  301. return render(request, 'eventos/crear_evento.html', {'form': form})
  302. @user_passes_test(lambda u: u.is_staff)
  303. def editar_evento(request, evento_id):
  304. evento = get_object_or_404(Evento, id=evento_id)
  305. if request.method == 'POST':
  306. form = EventoForm(request.POST, instance=evento)
  307. if form.is_valid():
  308. form.save()
  309. return redirect('lista_eventos')
  310. else:
  311. form = EventoForm(instance=evento)
  312. return render(request, 'crear_evento.html', {'form': form, 'evento': evento})
  313. @login_required
  314. def apuntar_lista_espera(request, evento_id):
  315. evento = get_object_or_404(Evento, id=evento_id)
  316. # Verificar si el usuario ya está en la lista de espera
  317. if ListaEspera.objects.filter(evento=evento, usuario=request.user).exists():
  318. messages.error(request, 'Ya estás en la lista de espera para este evento.')
  319. return redirect('eventos:lista_eventos')
  320. # Crear una entrada en la lista de espera
  321. if request.method == 'POST':
  322. form = ListaEsperaForm(request.POST)
  323. if form.is_valid():
  324. ListaEspera.objects.create(evento=evento, usuario=request.user)
  325. # enviar correos de confirmación y aviso al administrador
  326. enviar_correo_lista_espera(evento, request.user)
  327. messages.success(request, 'Te has apuntado a la lista de espera para este evento.')
  328. return redirect('eventos:lista_eventos')
  329. form = ListaEsperaForm()
  330. return render(request, 'eventos/apuntar_lista_espera.html', {'form': form, 'evento': evento})
  331. @login_required
  332. def apuntar_lista_espera_parejas(request, evento_id):
  333. evento = get_object_or_404(Evento, id=evento_id)
  334. # Verificar si el usuario ya está en la lista de espera
  335. if ListaEspera.objects.filter(evento=evento, usuario=request.user).exists():
  336. messages.error(request, 'Ya estás en la lista de espera para este evento.')
  337. return redirect('eventos:lista_eventos')
  338. # Crear una entrada en la lista de espera
  339. if request.method == 'POST':
  340. form = ListaEsperaForm(request.POST, evento=evento, usuario=request.user)
  341. if form.is_valid():
  342. acompanante = form.cleaned_data.get('acompanante')
  343. ListaEspera.objects.create(
  344. evento=evento,
  345. usuario=request.user,
  346. acompanante=acompanante if evento.para_parejas else None
  347. )
  348. # enviar correos de confirmación y aviso al administrador
  349. enviar_correo_lista_espera(evento, request.user,
  350. acompanante=acompanante if evento.para_parejas else None)
  351. messages.success(request, 'Te has apuntado a la lista de espera para este evento.')
  352. return redirect('eventos:lista_eventos')
  353. else:
  354. form = ListaEsperaForm(evento=evento, usuario=request.user)
  355. return render(request, 'eventos/apuntar_lista_espera.html', {'form': form, 'evento': evento})
  356. #
  357. # API
  358. #
  359. @api_view(['GET'])
  360. def api_lista_eventos(request):
  361. """Devuelve la lista de todos los eventos."""
  362. eventos = Evento.objects.all()
  363. serializer = EventoSerializer(eventos, many=True)
  364. return Response(serializer.data)
  365. @api_view(['GET'])
  366. def api_detalle_evento(request, evento_id):
  367. """Devuelve los detalles de un evento específico."""
  368. try:
  369. evento = Evento.objects.get(id=evento_id)
  370. serializer = EventoSerializer(evento)
  371. return Response(serializer.data)
  372. except Evento.DoesNotExist:
  373. return Response({'error': 'Evento no encontrado'}, status=404)
  374. @api_view(['GET'])
  375. def api_lista_reservas(request):
  376. """Devuelve la lista de todos los reservas."""
  377. reservas = Reserva.objects.all()
  378. serializer = ReservaSerializer(reservas, many=True)
  379. return Response(serializer.data)
  380. @api_view(['GET'])
  381. def api_detalle_reserva(request, reserva_id):
  382. """Devuelve los detalles de un reserva específico."""
  383. try:
  384. reserva = Reserva.objects.get(id=reserva_id)
  385. serializer = ReservaSerializer(reserva)
  386. return Response(serializer.data)
  387. except Reserva.DoesNotExist:
  388. return Response({'error': 'Reserva no encontrado'}, status=404)
  389. @api_view(['GET'])
  390. def api_lista_listaespera(request):
  391. """Devuelve la lista de todos los listaespera."""
  392. listaesperas = ListaEspera.objects.all()
  393. serializer = ListaEsperaSerializer(listaesperas, many=True)
  394. return Response(serializer.data)
  395. @api_view(['GET'])
  396. def api_detalle_listaespera(request, listaespera_id):
  397. """Devuelve los detalles de un listaespera específico."""
  398. try:
  399. listaespera = ListaEspera.objects.get(id=listaespera_id)
  400. serializer = ListaEsperaSerializer(listaespera)
  401. return Response(serializer.data)
  402. except ListaEspera.DoesNotExist:
  403. return Response({'error': 'ListaEspera no encontrado'}, status=404)
  404. @api_view(['GET'])
  405. def api_lista_noticias(request):
  406. """Devuelve la lista de todos los noticias."""
  407. noticias = Noticia.objects.all()
  408. serializer = NoticiaSerializer(noticias, many=True)
  409. return Response(serializer.data)
  410. @api_view(['GET'])
  411. def api_detalle_noticia(request, noticia_id):
  412. """Devuelve los detalles de un noticia específico."""
  413. try:
  414. noticia = Noticia.objects.get(id=noticia_id)
  415. serializer = NoticiaSerializer(noticia)
  416. return Response(serializer.data)
  417. except Noticia.DoesNotExist:
  418. return Response({'error': 'Noticia no encontrado'}, status=404)
  419. # Verifica si el usuario es administrador
  420. def es_admin(user):
  421. return user.is_staff
  422. @user_passes_test(es_admin) # Solo administradores pueden acceder
  423. def enviar_correo_inscritos_html(request, evento_id):
  424. """
  425. Envía un correo HTML a los inscritos en un evento
  426. """
  427. evento = get_object_or_404(Evento, id=evento_id)
  428. inscritos = Reserva.objects.filter(evento=evento).select_related('usuario')
  429. if not inscritos:
  430. messages.warning(request, "No hay usuarios inscritos en este evento.")
  431. return redirect('eventos:detalle_evento', evento_id=evento.id)
  432. destinatarios = [reserva.usuario.email for reserva in inscritos if reserva.usuario.email]
  433. if not destinatarios:
  434. messages.warning(request, "No hay correos disponibles para los inscritos.")
  435. return redirect('eventos:detalle_evento', evento_id=evento.id)
  436. if request.method == "POST":
  437. form = MensajeCorreoForm(request.POST)
  438. if form.is_valid():
  439. asunto_usuario = form.cleaned_data['asunto']
  440. mensaje_usuario = form.cleaned_data['mensaje']
  441. asunto = f"{asunto_usuario} - {evento.nombre}"
  442. from_email = settings.DEFAULT_FROM_EMAIL
  443. for usuario in destinatarios:
  444. html_content = render_to_string(
  445. 'emails/correo_a_inscritos.html',
  446. {'evento': evento,
  447. 'usuario': usuario,
  448. 'mensaje_usuario': mensaje_usuario,
  449. 'asunto_usuario': asunto_usuario}
  450. )
  451. msg = EmailMultiAlternatives(
  452. asunto,
  453. mensaje_usuario,
  454. from_email,
  455. [usuario],
  456. )
  457. msg.attach_alternative(html_content, "text/html")
  458. msg.send()
  459. messages.success(request, f"Correo enviado a {len(destinatarios)} inscritos.")
  460. return redirect('eventos:detalle_evento', evento_id=evento.id)
  461. else:
  462. # Crear texto por defecto con los datos del evento (formato HTML)
  463. asunto_defecto = f"Información sobre {evento.nombre}"
  464. mensaje_defecto = f"""<p>Hola a todos,</p>
  465. <p>Os escribimos para daros información sobre el pozo:</p>
  466. <ul>
  467. <li><strong>Descripción:</strong> {evento.nombre}</li>
  468. <li><strong>Fecha:</strong> {evento.fecha.strftime('%d/%m/%Y')} a las {evento.hora}</li>
  469. <li><strong>Precio:</strong> {evento.precio} € por persona</li>
  470. </ul>
  471. """
  472. if evento.lugar:
  473. mensaje_defecto += f'<p><strong>Lugar:</strong> <a href="{ evento.lugar.url_ubicacion }" target="_blank">{ evento.lugar.nombre }</a> ({ evento.lugar.notas })</p>'
  474. mensaje_defecto += "<p>Por favor se ruega y se pide <strong>puntualidad</strong>.</p>"
  475. form = MensajeCorreoForm(initial={
  476. 'asunto': asunto_defecto,
  477. 'mensaje': mensaje_defecto
  478. })
  479. return render(request, 'eventos/enviar_correo_inscritos.html', {'form': form, 'evento': evento})
  480. @login_required
  481. def estadisticas_por_usuario(request):
  482. today = date.today()
  483. mes_comienzo_temporada = settings.MES_COMIENZO_TEMPORADA
  484. if today.month < mes_comienzo_temporada:
  485. filtro_fecha = date(today.year - 1, mes_comienzo_temporada, 1)
  486. else:
  487. filtro_fecha = date(today.year, mes_comienzo_temporada, 1)
  488. reservas = Reserva.objects.filter(evento__fecha__gte=filtro_fecha)
  489. datos = (
  490. reservas.values('usuario__id', 'usuario__email', 'usuario__nombre')
  491. .annotate(eventos_inscritos=Count('evento', distinct=True))
  492. .order_by('-eventos_inscritos')
  493. )
  494. usuarios = User.objects.all().annotate(
  495. eventos_inscritos=Count(
  496. 'reserva__evento',
  497. filter=Q(reserva__evento__fecha__gte=filtro_fecha),
  498. distinct=True)
  499. ).order_by('-eventos_inscritos')
  500. return render(request, 'eventos/estadisticas_usuarios.html', {
  501. 'datos': usuarios,
  502. 'desde': filtro_fecha,
  503. })
  504. @login_required
  505. def eventos_por_usuario(request, usuario_id):
  506. usuario = get_object_or_404(User, pk=usuario_id)
  507. reservas = Reserva.objects.filter(usuario=usuario).select_related("evento")
  508. return render(request, "eventos/eventos_por_usuario.html", {
  509. "usuario": usuario,
  510. "reservas": reservas
  511. })
  512. @staff_member_required
  513. def cancelar_reserva_admin(request, reserva_id):
  514. reserva = get_object_or_404(Reserva, id=reserva_id)
  515. usuario_id = reserva.usuario.id
  516. if request.method == "POST":
  517. reserva.delete()
  518. return redirect("eventos:eventos_por_usuario", usuario_id)