소스 검색

Merge branch 'nuevoscampos' of https://forgejo.reymota.es/creylopez/JugarAlPadelOC into nuevoscampos

Tino 6 달 전
부모
커밋
8cc615d97c

BIN
Instrucciones Parejas.pages


+ 2 - 56
README.md

@@ -1,14 +1,6 @@
-# Versión para compatible con OKD y K8S
+# JugaralPadelOC
 
-## Instalación
-
-### Crear un proyecto
-
-    oc new-project jugaralpadel
-
-#### Permitir que los pods puedan funcionar como root
-
-    oc adm policy add-scc-to-user anyuid -z default
+Lo de OC viene porque creé la aplicación en Openshift. Ya no uso Openshift.
 
 ## Cosas a hacer la primera vez
 Desde dentro de la shell del pod
@@ -26,52 +18,6 @@ ALTER SEQUENCE tablename_id_seq RESTART WITH nn;
 
 esto se hace cuando restauro un volcado de la bd sobre una instalación nueva. Si hay índices ya creados, hay que reinciar a partir del último.
 
-
-## Changing the default storage class 
-
-
-Use the following process to change the default storage class. For example you have two defined storage classes, gp2 and standard, and you want to change the default storage class from gp2 to standard.
-
-List the storage class:
-
-    oc get storageclass
-
-Example output
-
-    NAME                 TYPE
-    gp2 (default)        kubernetes.io/aws-ebs  1 
-    standard             kubernetes.io/aws-ebs
-
-1. (default) denotes the default storage class.
-
-Change the value of the ```storageclass.kubernetes.io/is-default-class``` annotation to **false** for the default storage class:
-
-    oc patch storageclass gp2 -p '{"metadata": {"annotations": {"storageclass.kubernetes.io/is-default-class": "false"}}}'
-
- 
-Make another storage class the default by setting the ```storageclass.kubernetes.io/is-default-class``` annotation to **true**:
-
-    oc patch storageclass standard -p '{"metadata": {"annotations": {"storageclass.kubernetes.io/is-default-class": "true"}}}'
-
-
-Verify the changes:
-
-    oc get storageclass
-
-Example output
-
-    NAME                 TYPE
-    gp2                  kubernetes.io/aws-ebs
-    standard (default)   kubernetes.io/aws-ebs
-
-
-
-## Cambiar la secuencia de lo sid
-
-    ALTER SEQUENCE tablename_id_seq RESTART WITH nn;
-
-esto se hace cuando restauro un volcado de la bd sobre una instalación nueva. Si hay índices ya creados, hay que reinciar a partir del último.
-
 ## Eventos de parejas
 
 Cuando se crea un evento, se puede indicar que es para parejas.

+ 0 - 6
src/entornoPruebas.sh

@@ -1,6 +0,0 @@
-export CSRF_TRUSTED_ORIGINS="http://localhost"
-export DEBUG="True"
-export SECRET_KEY="hola"
-export DJANGO_ALLOWED_HOSTS="vmcluster localhost imac.reymota.lab"
-
-

+ 12 - 2
src/eventos/forms.py

@@ -1,5 +1,6 @@
 from django import forms
 from django.contrib.auth import get_user_model
+from tinymce.widgets import TinyMCE
 
 from .models import Evento
 from .models import ListaEspera
@@ -56,11 +57,12 @@ class ReservaForm(forms.ModelForm):
 class EventoForm(forms.ModelForm):
     class Meta:
         model = Evento
-        fields = ['nombre', 'descripcion', 'fecha', 'hora', 'plazas_disponibles', 'publicado', 'para_parejas', 'lugar', 'url_imagen']
+        fields = ['nombre', 'descripcion', 'fecha', 'hora', 'plazas_disponibles', 'precio', 'publicado', 'para_parejas', 'lugar', 'url_imagen']
 
         widgets = {
             'fecha': forms.DateInput(attrs={'type': 'date'}),
             'hora': forms.TimeInput(attrs={'type': 'time'}),
+            'url_imagen': forms.URLInput(attrs={'class': 'form-control'}),
         }
 
 
@@ -116,6 +118,14 @@ class MensajeCorreoForm(forms.Form):
 
     mensaje = forms.CharField(
         label="Mensaje",
-        widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 16}),
+        widget=TinyMCE(attrs={'cols': 80, 'rows': 25}),
         required=True
     )
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        # Asegurar que los valores iniciales se muestren en los widgets
+        if self.initial.get('asunto'):
+            self.fields['asunto'].widget.attrs['value'] = self.initial['asunto']
+        if self.initial.get('mensaje'):
+            self.fields['mensaje'].widget.attrs['value'] = self.initial['mensaje']

+ 8 - 0
src/eventos/models.py

@@ -14,6 +14,7 @@ class Evento(models.Model):
     para_parejas = models.BooleanField(default=False)  # 🔹 nuevo campo para parejas
     lugar = models.ForeignKey('Lugar', on_delete=models.CASCADE, null=True, blank=True)
     # lugar = models.ForeignKey('Lugar', on_delete=models.CASCADE)
+    precio = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
 
     def __str__(self):
         return self.nombre
@@ -56,6 +57,9 @@ class ListaEspera(models.Model):
     )
     fecha_apuntado = models.DateTimeField(auto_now_add=True)
 
+    class Meta:
+        verbose_name_plural = "Listas de espera"
+
     def __str__(self):
         return f'{self.usuario.nombre} en lista de espera para {self.evento.nombre}. Solicitada plaza el: {self.fecha_apuntado}'
 
@@ -77,6 +81,10 @@ class Noticia(models.Model):
 class Lugar(models.Model):
     nombre = models.CharField(max_length=200)
     url_ubicacion = models.URLField()
+    notas = models.TextField(blank=True, null=True)
+
+    class Meta:
+        verbose_name_plural = "Lugares"
 
     def __str__(self):
         return self.nombre

+ 0 - 1
src/eventos/urls.py

@@ -5,7 +5,6 @@ from .views import api_lista_eventos, api_detalle_evento
 from .views import api_lista_reservas, api_detalle_reserva
 from .views import api_lista_listaespera, api_detalle_listaespera
 from .views import api_lista_noticias, api_detalle_noticia
-from .views import enviar_correo_inscritos
 from .views import enviar_correo_inscritos_html
 
 app_name = 'eventos'

+ 24 - 50
src/eventos/views.py

@@ -61,6 +61,7 @@ def enviar_correo_reserva(evento, usuario, acompanante=None):
     texto += f'Nombre: {evento.nombre}\n'
     texto += f'Descripción: {evento.descripcion}\n'
     texto += f'Fecha: {evento.fecha.strftime("%d/%b/%y")}  a las {evento.hora}\n'
+    texto += f'Precio: {evento.precio} €'
 
     if acompanante:
         texto += f"\nHas indicado como pareja a {acompanante.nombre}.\n"
@@ -341,6 +342,7 @@ def detalle_evento(request, evento_id):
             'descripcion': evento.descripcion,
             'fecha': evento.fecha,
             'hora': evento.hora,
+            'precio': evento.precio,
             'plazas_disponibles': evento.plazas_disponibles,
             'plazas_restantes': evento.plazas_restantes,
             'en_espera': en_espera,
@@ -523,55 +525,6 @@ def api_detalle_noticia(request, noticia_id):
 def es_admin(user):
     return user.is_staff
 
-
-@user_passes_test(es_admin)  # Solo administradores pueden acceder
-def enviar_correo_inscritos(request, evento_id):
-    evento = get_object_or_404(Evento, id=evento_id)
-    inscritos = Reserva.objects.filter(evento=evento).select_related('usuario')
-
-    if not inscritos:
-        messages.warning(request, "No hay usuarios inscritos en este evento.")
-        return redirect('eventos:detalle_evento', evento_id=evento.id)
-
-    destinatarios = [reserva.usuario.email for reserva in inscritos if reserva.usuario.email]
-
-    if not destinatarios:
-        messages.warning(request, "No hay correos disponibles para los inscritos.")
-        return redirect('eventos:detalle_evento', evento_id=evento.id)
-
-    if request.method == "POST":
-        form = MensajeCorreoForm(request.POST)
-        if form.is_valid():
-            asunto_usuario = form.cleaned_data['asunto']
-            mensaje_usuario = form.cleaned_data['mensaje']
-
-            asunto = f"{asunto_usuario} - {evento.nombre}"
-
-            firma = "\n\n\nSaludos, \n\nPádel entre amigos"
-            mensaje = f"{mensaje_usuario}{firma}"
-
-            mensajes = [
-                (asunto, mensaje, settings.DEFAULT_FROM_EMAIL, [email]) for email in destinatarios
-            ]
-
-            try:
-                send_mass_mail(mensajes, fail_silently=False)
-            except BadHeaderError:
-                return HttpResponse('Encabezado inválido encontrado.')
-            except SMTPException as e:
-                return HttpResponse(f'Error de SMTP: {e}')
-            except Exception as e:
-                return HttpResponse(f'Ocurrió un error: {e}')
-
-            logger.debug('Correo enviado')
-
-            messages.success(request, f"Correo enviado a {len(destinatarios)} inscritos.")
-            return redirect('eventos:detalle_evento', evento_id=evento.id)
-    else:
-        form = MensajeCorreoForm()
-
-    return render(request, 'eventos/enviar_correo_inscritos.html', {'form': form, 'evento': evento})
-
 @user_passes_test(es_admin)  # Solo administradores pueden acceder
 def enviar_correo_inscritos_html(request, evento_id):
     """
@@ -623,7 +576,28 @@ def enviar_correo_inscritos_html(request, evento_id):
             return redirect('eventos:detalle_evento', evento_id=evento.id)
             
     else:
-        form = MensajeCorreoForm()
+        # Crear texto por defecto con los datos del evento (formato HTML)
+        asunto_defecto = f"Información sobre {evento.nombre}"
+        mensaje_defecto = f"""<p>Hola a todos,</p>
+
+<p>Os escribimos para daros información sobre el pozo:</p>
+
+<ul>
+    <li><strong>Descripción:</strong> {evento.nombre}</li>
+    <li><strong>Fecha:</strong> {evento.fecha.strftime('%d/%m/%Y')} a las {evento.hora}</li>
+    <li><strong>Precio:</strong> {evento.precio} € por persona</li>
+</ul>
+"""
+        
+        if evento.lugar:
+            mensaje_defecto += f'<p><strong>Lugar:</strong> <a href="{ evento.lugar.url_ubicacion }" target="_blank">{ evento.lugar.nombre }</a> ({ evento.lugar.notas })</p>'
+
+        mensaje_defecto += "<p>Por favor se ruega y se pide <strong>puntualidad</strong>.</p>"
+        
+        form = MensajeCorreoForm(initial={
+            'asunto': asunto_defecto,
+            'mensaje': mensaje_defecto
+        })
 
     return render(request, 'eventos/enviar_correo_inscritos.html', {'form': form, 'evento': evento})
 

+ 25 - 0
src/gestion_reservas/settings.py

@@ -54,6 +54,7 @@ INSTALLED_APPS = [
     'reymotausers',
 
     'rest_framework',
+    'tinymce',
 ]
 
 MIDDLEWARE = [
@@ -241,3 +242,27 @@ LOGGING = {
         },
     },
 }
+
+TINYMCE_DEFAULT_CONFIG = {
+    'height': 360,
+    'width': 1120,
+    'cleanup_on_startup': True,
+    'custom_undo_redo_levels': 20,
+    'selector': 'textarea',
+    'plugins': '''
+            textcolor save link image media preview codesample contextmenu
+            table code lists fullscreen insertdatetime nonbreaking
+            contextmenu directionality searchreplace wordcount visualblocks
+            visualchars code fullscreen autolink lists charmap print hr
+            anchor pagebreak
+            ''',
+    'toolbar1': '''
+            fullscreen preview bold italic underline | fontselect,
+            fontsizeselect | forecolor backcolor | alignleft alignright |
+            aligncenter alignjustify | indent outdent | bullist numlist table |
+            ''',
+    'contextmenu': 'formats | link image',
+    'menubar': True,
+    'statusbar': True,
+    'promotion': False,
+}

+ 2 - 0
src/gestion_reservas/urls.py

@@ -33,6 +33,8 @@ urlpatterns = [
     path("accounts/", include("accounts.urls")),  # new
     path("accounts/", include("django.contrib.auth.urls")),
 
+    path('tinymce/', include('tinymce.urls')),  # TinyMCE URLs
+
     path('', views.principal, name='principal'),
     path('entorno/', views.ver_variables_entorno, name='ver_variables_entorno'),
     path('ayuda/', views.ayuda, name='ayuda'),

+ 1 - 0
src/requirements.txt

@@ -25,3 +25,4 @@ weasyprint==68.0
 webencodings==0.5.1
 zopfli==0.4.0
 whitenoise
+django-tinymce==4.1.0

+ 2 - 0
src/templates/_head.html

@@ -19,5 +19,7 @@
     
     <!-- App CSS -->  
     <link id="theme-style" rel="stylesheet" href="{% static 'css/portal.css' %}">
+    
+    {{ form.media }}
 
 </head> 

+ 3 - 1
src/templates/emails/confirmacion_reserva.html

@@ -50,7 +50,9 @@
         <p>Hola, {{ usuario.nombre }},</p>
         <p>Te has inscrito correctamente en el evento <strong>{{ evento.nombre }}</strong>.</p>
         <p><strong>Descripción:</strong> {{ evento.descripcion }}</p>
-        <p><strong>Fecha:</strong> {{ evento.fecha }} a las {{ evento.hora}} </p>
+        <p><strong>Fecha:</strong> {{ evento.fecha }} a las {{ evento.hora}}.</p>
+        <p><strong> Precio:</strong> {{ evento.precio }} € por persona.</p>
+
         {% if evento.lugar %}
             <p><strong>Lugar:</strong> <a href="{{ evento.lugar.url_ubicacion }}" target="_blank">{{ evento.lugar.nombre }}</a></p>
         {% endif %}

+ 2 - 1
src/templates/emails/confirmacion_reserva_parejas.html

@@ -49,7 +49,8 @@
         <p>Hola, {{ usuario.nombre }} y {{ acompanante.nombre }},</p>
         <p>Os habéis inscrito correctamente en el evento <strong>{{ evento.nombre }}</strong>.</p>
         <p><strong>Descripción:</strong> {{ evento.descripcion }}</p>
-        <p><strong>Fecha:</strong> {{ evento.fecha }} a las {{ evento.hora}} </p>
+        <p><strong>Fecha:</strong> {{ evento.fecha }} a las {{ evento.hora}}.</p>
+        <p><strong> Precio:</strong> {{ evento.precio }} € por persona.</p>
         {% if evento.lugar %}
             <p><strong>Lugar:</strong> <a href="{{ evento.lugar.url_ubicacion }}" target="_blank">{{ evento.lugar.nombre }}</a></p>
         {% endif %}

+ 1 - 2
src/templates/emails/correo_a_inscritos.html

@@ -47,8 +47,7 @@
     </div>
     <div class="cuerpo">
         <h1>{{ asunto_usuario }}</h1>
-        <p>Hola,</p>
-        <p>{{ mensaje_usuario }}</p>
+        <p>{{ mensaje_usuario|safe }}</p>
         <p><strong>IMPORTANTE:</strong> Para gestionar cambios o bajas NO contestes a este correo, escribe al administrador por WhatsApp</p>
     </div>
 </body>

+ 100 - 10
src/templates/eventos/crear_evento.html

@@ -5,17 +5,107 @@
 {% endblock menuapp %}
 
 {% block content %}
-<div class="column is-4 is-offset-4">
-
-    <h3>{% if evento %}Editar evento{% else %}Nuevo evento{% endif %}</h3>
-    <div class="box">
-        <form method="POST" enctype="multipart/form-data">
-            {% csrf_token %}
-            {{ form.as_p }}
-            <div class="text mb-3">
-                <button type="submit" class="btn app-btn-primary w-100 theme-btn mx-auto">{% if evento %}Actualizar{% else %}Crear{% endif %} Evento</button>
+<div class="container">
+    <div class="row justify-content-center">
+        <div class="col-md-8">
+            <h3>{% if evento %}Editar evento{% else %}Nuevo evento{% endif %}</h3>
+            <div class="card">
+                <div class="card-body">
+                    <form method="POST" enctype="multipart/form-data">
+                        {% csrf_token %}
+                        
+                        <div class="mb-3">
+                            <label for="{{ form.nombre.id_for_label }}" class="form-label">{{ form.nombre.label }}</label>
+                            {{ form.nombre }}
+                            {% if form.nombre.errors %}
+                                <div class="text-danger">{{ form.nombre.errors }}</div>
+                            {% endif %}
+                        </div>
+
+                        <div class="mb-3">
+                            <label for="{{ form.descripcion.id_for_label }}" class="form-label">{{ form.descripcion.label }}</label>
+                            {{ form.descripcion }}
+                            {% if form.descripcion.errors %}
+                                <div class="text-danger">{{ form.descripcion.errors }}</div>
+                            {% endif %}
+                        </div>
+
+                        <div class="row">
+                            <div class="col-md-6">
+                                <div class="mb-3">
+                                    <label for="{{ form.fecha.id_for_label }}" class="form-label">{{ form.fecha.label }}</label>
+                                    {{ form.fecha }}
+                                    {% if form.fecha.errors %}
+                                        <div class="text-danger">{{ form.fecha.errors }}</div>
+                                    {% endif %}
+                                </div>
+                            </div>
+                            <div class="col-md-6">
+                                <div class="mb-3">
+                                    <label for="{{ form.hora.id_for_label }}" class="form-label">{{ form.hora.label }}</label>
+                                    {{ form.hora }}
+                                    {% if form.hora.errors %}
+                                        <div class="text-danger">{{ form.hora.errors }}</div>
+                                    {% endif %}
+                                </div>
+                            </div>
+                        </div>
+
+                        <div class="mb-3">
+                            <label for="{{ form.plazas_disponibles.id_for_label }}" class="form-label">{{ form.plazas_disponibles.label }}</label>
+                            {{ form.plazas_disponibles }}
+                            {% if form.plazas_disponibles.errors %}
+                                <div class="text-danger">{{ form.plazas_disponibles.errors }}</div>
+                            {% endif %}
+                        </div>
+
+                        <div class="mb-3">
+                            <label for="{{ form.precio.id_for_label }}" class="form-label">{{ form.precio.label }}</label>
+                            {{ form.precio }}
+                            {% if form.precio.errors %}
+                                <div class="text-danger">{{ form.precio.errors }}</div>
+                            {% endif %}
+                        </div>
+
+                        <div class="mb-3">
+                            <label for="{{ form.lugar.id_for_label }}" class="form-label">{{ form.lugar.label }}</label>
+                            {{ form.lugar }}
+                            {% if form.lugar.errors %}
+                                <div class="text-danger">{{ form.lugar.errors }}</div>
+                            {% endif %}
+                        </div>
+
+                        <div class="mb-3">
+                            <label for="{{ form.url_imagen.id_for_label }}" class="form-label">{{ form.url_imagen.label }}</label>
+                            {{ form.url_imagen }}
+                            {% if form.url_imagen.errors %}
+                                <div class="text-danger">{{ form.url_imagen.errors }}</div>
+                            {% endif %}
+                        </div>
+
+                        <div class="mb-3 form-check">
+                            {{ form.publicado }}
+                            <label for="{{ form.publicado.id_for_label }}" class="form-check-label">{{ form.publicado.label }}</label>
+                            {% if form.publicado.errors %}
+                                <div class="text-danger">{{ form.publicado.errors }}</div>
+                            {% endif %}
+                        </div>
+
+                        <div class="mb-3 form-check">
+                            {{ form.para_parejas }}
+                            <label for="{{ form.para_parejas.id_for_label }}" class="form-check-label">{{ form.para_parejas.label }}</label>
+                            {% if form.para_parejas.errors %}
+                                <div class="text-danger">{{ form.para_parejas.errors }}</div>
+                            {% endif %}
+                        </div>
+
+                        <div class="text-center">
+                            <button type="submit" class="btn btn-primary btn-lg">{% if evento %}Actualizar{% else %}Crear{% endif %} Evento</button>
+                        </div>
+                    </form>
+                </div>
             </div>
-        </form>
+        </div>
     </div>
 </div>
 {% endblock %}

+ 2 - 0
src/templates/eventos/detalle_evento.html

@@ -38,6 +38,8 @@
                         <li class="list-inline-item">Fecha: {{ evento.fecha }}</li>
                         <li class="list-inline-item"> a las {{ evento.hora }}</li>
                         <li class="list-inline-item">|</li>
+                        <li class="list-inline-item"> Precio {{ evento.precio }} €</li>
+                        <li class="list-inline-item">|</li>
                         <li class="list-inline-item">Plazas: {{ evento.plazas_disponibles }}. Quedan: {{ evento.plazas_restantes }}</li>
 		        <li class="list-inline-item">|</li>
                         <li class="list-inline-item">Lugar: <a href="{{ evento.lugar.url_ubicacion }}" target="_blank">{{ evento.lugar.nombre }}</a></li>