+6 −6
Original line number Diff line number Diff line
@@ -83,10 +83,8 @@ def crear_clase(request):
@login_required
@user_passes_test(lambda user: user.user_type in ['instructor','admin'] or user.is_superuser)
def editar_clase(request):
    clase = Clase.objects.get(id=request.POST.get('clase'))
    if request.POST.get('archivo_pdf')!='':
        clase.archivo_pdf.delete()
    if request.method == 'POST':
        clase = Clase.objects.get(id=request.POST.get('clase'))
        form = ClaseForm(request.POST, request.FILES, instance=clase)
        if form.is_valid():
            form.save()
@@ -96,6 +94,8 @@ def editar_clase(request):
        else:
            return JsonResponse({'success': False, 'errors': form.errors})
    else:
        clase_id = request.POST.get('clase')
        clase = Clase.objects.get(id=clase_id)
        form = ClaseForm(instance=clase)
        return render(request, 'clase/editar_clase.html', {'form': form})

+15 −14
Original line number Diff line number Diff line
{% extends 'base_externa.html' %} 
{% load static %}




{% block contenido %}

<!-- Apartado para mostrar los cursos disponibles -->
{% if pagination %}
<div class="container-xxl py-5">
    <div class="container">
      <div class="text-center wow fadeInUp" data-wow-delay="0.1s">
        <h6 class="section-title bg-white text-center text-primary px-3">Cursos</h6>
        <h1 class="mb-5">Cursos disponibles para ti</h1>
      </div>
      <div class="row g-4 justify-content-center">
@@ -37,18 +34,22 @@
      </div>
    </div>
  </div>

  <!-- FIN del apartado para mostrar los cursos disponibles -->

{% else %}
<div id="class-message" data-class="0" class="container-xxl py-5 wow fadeInUp" data-wow-delay="0.1s">
    <div class="container text-center">
        <div class="row justify-content-center">
            <div class="col-lg-6">
                <i class="bi bi-emoji-frown-fill display-1 text-primary"></i>
                <h1 class="mb-4">No hay cursos disponibles por el momento</h1>
            </div>
        </div>
    </div>
</div>
{% endif %}

{% endblock contenido %}



{% block pag %} 
 <!-- Paginacion de los cursos -->
 {% include 'paginacion.html' %}
 <!-- Fin de la paginacion de los cursos -->
   
{% endblock pag%}
{% block pag %} {% if pagination %} {% include 'paginacion.html' %} {% endif %} {% endblock pag%}
+46 −27
Original line number Diff line number Diff line
{% extends "base_externa.html" %}
{% load static %}


<!--  Barra de busqueda  -->
{% block nav_externo %} {% include 'barra_de_busqueda_de_estudiante_logeado.html' %} {% endblock nav_externo %}
<!-- Fin de la barra de busqueda -->

{% block contenido %}
  <div class="container-xxl px-5 py-4">
    <div class="container">
      {% if cursos.paginator.count > 0 %}
        <div class="text-center mb-3">
          <h6 class="section-title bg-white text-primary px-3">Cursos</h6>
          <h1>Cusos inscritos</h1>
          <h1>Cursos inscritos</h1>
        </div>
        <div class="row g-3 justify-content-center">
          {% for curso in cursos %}
@@ -22,7 +17,7 @@
                  <img class="img-fluid" src="{{curso.miniatura.url}}" style="width: 500px; height: 200px;">
                  <div class="w-100 d-flex justify-content-center position-absolute bottom-0 start-0 mb-4">
                    <a class="flex-shrink-0 btn btn-sm btn-primary px-3 border-end" style="border-radius: 30px 0 0 30px;" href="{% url 'curso:ver_curso' curso.id %}">Leer más</a>
                    <a class="flex-shrink-0 btn btn-sm btn-danger px-3" href="{% url 'curso:eliminar_suscripcion' curso.id %}">Desinscribirme</a>
                    <a class="flex-shrink-0 btn btn-sm btn-danger px-3" href="{% url 'curso:eliminar_suscripcion' curso.id %}" data-bs-toggle="modal" data-bs-target="#confirmacionDesinscripcion{{ curso.id }}">Desinscribirme</a>
                    <a href="{% url 'clase:clases_estudiante' curso.id  %}" class="flex-shrink-0 btn btn-sm btn-primary px-3" style="border-radius: 0 30px 30px 0;">Ver clases</a>
                  </div>
                </div>
@@ -36,19 +31,43 @@
                </div>
              </div>
            </div>

            <!-- Modal de Confirmación Desinscripción -->
            <div class="modal fade" id="confirmacionDesinscripcion{{ curso.id }}" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
              <div class="modal-dialog">
                <div class="modal-content">
                  <div class="modal-header">
                    <h5 class="modal-title" id="exampleModalLabel">Confirmación</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                  </div>
                  <div class="modal-body">
                    ¿Estás seguro de que quieres desinscribirte de este curso?
                  </div>
                  <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
                    <a class="btn btn-danger" href="{% url 'curso:eliminar_suscripcion' curso.id %}">Desinscribirme</a>
                  </div>
                </div>
              </div>
            </div>
              <!-- Modal de Confirmación Desinscripción -->
          {% endfor %}
        </div>

        {% else %}
        <center>
            <h2>Aún no estás inscrito en ningún curso</h2> 
        </center>
        <div id="class-message" data-class="0" class="container-xxl py-5 wow fadeInUp" data-wow-delay="0.1s">
            <div class="container text-center">
                <div class="row justify-content-center">
                    <div class="col-lg-6">
                        <i class="bi bi-emoji-frown-fill display-1 text-primary"></i>
                        <h1 class="mb-4">No hay cursos disponibles por el momento</h1>
                    </div>
                </div>
            </div>
        </div>
        {% endif %}
    </div>
  </div>
{% endblock contenido %}


{% block pag %}
{% include 'paginacion.html' %}
{% endblock pag %}
 No newline at end of file
  {% endblock contenido %}
{% block pag %} {% if pagination %} {% include 'paginacion.html' %} {% endif %} {% endblock pag %}
+28 −8
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@
  <div class="container">
    <div class="text-center wow fadeInUp" data-wow-delay="0.1s">
      <h6 class="section-title bg-white text-center text-primary px-3">Cursos</h6>
      <h1 class="mb-5">Cursos que has accedido</h1>
      <h1 class="mb-5">Cursos a los que te has inscrito</h1>
    </div>
    <div class="row g-4 justify-content-center">
      {% for curso in cursos_con_inscripcion %}
@@ -20,7 +20,7 @@
              <img class="img-fluid" src="{{curso.miniatura.url}}" style="width: 500px; height: 200px;">
              <div class="w-100 d-flex justify-content-center position-absolute bottom-0 start-0 mb-4">
                <a class="flex-shrink-0 btn btn-sm btn-primary px-3 border-end" style="border-radius: 30px 0 0 30px;" href="{% url 'curso:ver_curso' curso.id %}">Leer más</a>
                <a class="flex-shrink-0 btn btn-sm btn-danger px-3" href="{% url 'curso:eliminar_suscripcion' curso.id %}">Desinscribirme</a>
                <button class="flex-shrink-0 btn btn-sm btn-danger px-3" data-bs-toggle="modal" data-bs-target="#confirmacionDesinscripcion{{ curso.id }}">Desinscribirme</button>
                <a id="ver_clase" onclick="cursoActual({{ curso.id }},{{request.user.id}})" class="flex-shrink-0 btn btn-sm btn-primary px-3" style="border-radius: 0 30px 30px 0;">Ver clases</a>
              </div>
            </div>
@@ -38,8 +38,6 @@
    </div>
  </div>
</div>


{% endif %}

<div class="container-xxl py-5">
@@ -86,8 +84,30 @@
    </div>
  </div>
</div>

<!-- Modal de Confirmación Desinscripción -->
{% for curso in cursos_con_inscripcion %}
<div class="modal fade" id="confirmacionDesinscripcion{{ curso.id }}" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Confirmación</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        ¿Estás seguro de que quieres desinscribirte de este curso?
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
        <a class="btn btn-danger" href="{% url 'curso:eliminar_suscripcion' curso.id %}">Desinscribirme</a>
      </div>
    </div>
  </div>
</div>
{% endfor %}
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="{% static 'js/ultimo_curso_visto.js' %}"></script>


{% endblock contenido %}
+42 −23
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@
                    <th>Apellidos</th>
                    <th>Profesión</th>
                    <th>Correo electrónico</th>

                    <th class="w-1">Opciones</th>
                    <th class="w-1"></th>
                </tr>
@@ -38,7 +37,7 @@
                    <td>{{ curso_inscripcion.estudiante.profession }}</td>
                    <td>{{ curso_inscripcion.estudiante.email }}</td>
                    <td>
                        <a onclick="eliminarSuscripcion({{ curso_inscripcion.estudiante.id }}, {{ curso_inscripcion.curso.id }})" class="btn btn-outline-danger w-100">
                        <a data-bs-toggle="modal" data-bs-target="#eliminarModal{{ curso_inscripcion.estudiante.id }}" class="btn btn-outline-danger w-100">
                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-trash-off" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
                                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                                <path d="M3 3l18 18"></path>
@@ -52,6 +51,26 @@
                        </a>
                    </td>
                </tr>

                <!-- Modal para confirmar la eliminación -->
                <div class="modal fade" id="eliminarModal{{ curso_inscripcion.estudiante.id }}" tabindex="-1" aria-labelledby="eliminarModalLabel" aria-hidden="true">
                    <div class="modal-dialog">
                        <div class="modal-content">
                            <div class="modal-header">
                                <h5 class="modal-title" id="eliminarModalLabel">Confirmar eliminación</h5>
                                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                            </div>
                            <div class="modal-body">
                                ¿Estás seguro de que quieres eliminar la suscripción de {{ curso_inscripcion.estudiante.username }}?
                            </div>
                            <div class="modal-footer">
                                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
                                <button type="button" class="btn btn-danger" onclick="eliminarSuscripcion({{ curso_inscripcion.estudiante.id }}, {{ curso_inscripcion.curso.id }})">Eliminar</button>
                            </div>
                        </div>
                    </div>
                </div>

                {% empty %}
                <tr>
                    <td colspan="5">No hay estudiantes registrados.</td>
@@ -61,12 +80,12 @@
        </table>
    </div>
</div>

{% endblock contenido %}


{% block js %}
<script src="{% static 'js/eliminar_inscritos_admin.js' %}"></script>

{% endblock js %}
    {% block pagination %}  {% include 'paginacion.html' %} {% endblock pagination %}
 No newline at end of file

{% block pagination %}
{% include 'paginacion.html' %}
{% endblock pagination %}
+18 −0
Original line number Diff line number Diff line
<!-- Modal de Confirmación Desinscripción -->
<div class="modal fade" id="confirmacionDesinscripcion{{ curso.id }}" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel">Confirmación</h5>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="modal-body">
          ¿Estás seguro de que quieres desinscribirte de este curso?
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
          <a class="btn btn-danger" href="{% url 'curso:eliminar_suscripcion' curso.id %}">Desinscribirme</a>
        </div>
      </div>
    </div>
  </div>
 No newline at end of file
+38 −0
Original line number Diff line number Diff line
<div class="modal modal-blur fade" id="notificacion" tabindex="-1" role="dialog" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title">Enviar Aviso</h5>
          <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
        </div>
        <div class="card-body">
          <div id="error-container" class="mb-3 text-danger"></div>
          <form  id="form-notificacion">

            <!-- DIV para mostrar errores  -->
            <div id="div_error" class="alert alert-danger" hidden>
              <p id="error"></p>
            </div>
            <!-- FIN DEL DIV --> 

              {% csrf_token %}

              {{ form_notificacion.asunto }}
              <p style="color: red;font-size: 12px;"  id="id_notificacion_asunto" hidden></p>

              <br>

              {{ form_notificacion.descripcion }}
              <p style="color: red;font-size: 12px;"  id="id_notificacion_descripcion" hidden></p>

             
              <div class="form-footer">
                <button type="submit" class="btn btn-primary w-100">Enviar</button>
              </div>
              
          </form>
        </div>
      </div>
    </div>
  </div>
  
 No newline at end of file
+1 −1
Original line number Diff line number Diff line
@@ -77,7 +77,7 @@
      </div>
    </div>
  </div>
  <table class="table table-bordered table-bordered-all table-light">
  <table class="table table-bordered table-bordered-all" style="background-color: #D4E4F5; border-radius: 10px; border-color: white;">
    <tbody>
      {% for clase in clases %}
        <tr>
+28 −1
Original line number Diff line number Diff line
@@ -14,6 +14,15 @@
  </svg>
      Ver inscritos
    </a>

    <a href="#" class="btn btn-outline-primary btn-pill d-none d-sm-inline-block" data-bs-toggle="modal" data-bs-target="#notificacion" >
      Enviar notificación
    </a>

    <a href="{% url 'curso:ver_notificaciones' curso.id %}" class="btn btn-outline-primary btn-pill d-none d-sm-inline-block" >
      Ver Notificaciones
    </a>

  </span>

 {% endif %}
@@ -250,7 +259,7 @@
                  </span>
                  {% if request.user.user_type == 'instructor' %}
                    <span class="col-auto">
                      <a class="bg-red-lt" data-bs-toggle="modal" data-bs-target="#confirmDeleteModal" > 
                      <a class="bg-red-lt" data-bs-toggle="modal" data-bs-target="#confirmDeleteExamen" data-examenid="{{ examen_datos.id }}" > 
                        <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-trash-x" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
                           <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
                           <path d="M4 7h16"></path>
@@ -278,5 +287,23 @@
  {% include 'examen/modal-examen.html' %}
  {% include 'clase/modal-clase.html' %}
  {% include 'clase/modal-eliminar_clase.html' %}
  {% include 'examen/modal_eliminar_examen.html' %}
  {% include 'clase/script-clase.html' %}
  {% include 'curso/modal_enviar_notificacion.html' %}
  {% include 'notificacion/script-notificacion.html' %}

  <script>
  var deleteExamenBtns = document.querySelectorAll('.bg-red-lt');
  deleteExamenBtns.forEach(function(btn) {
    btn.addEventListener('click', function(e) {
      e.preventDefault();
      var examenId = btn.getAttribute('data-examenid');
      var deleteUrl = '/examen/eliminar_examen/' + examenId ;

      var deleteExamenModalBtn = document.getElementById('deleteExamenBtn');
      deleteExamenModalBtn.setAttribute('href', deleteUrl);
    });
  });
</script>

{% endblock js %}
 No newline at end of file
+2 −0
Original line number Diff line number Diff line
@@ -22,4 +22,6 @@ urlpatterns = [
    path('todos_los_cursos/',views.cursos_disponibles_para_estudiante,name='todos_los_cursos'),
    path('inscrito_curso/<int:curso_id>/', views.alumnos_inscritos_curso, name='inscrito_curso'),
    path('eliminar_suscripcion/<int:curso_id>/',views.desinscribir_estudiantes,name='eliminar_suscripcion'),
    path('enviar_notificacion/curso/<int:curso_id>/',views.enviar_notificacion,name='enviar_notificacion'),
    path('ver_notificaciones/<int:curso_id>',views.ver_notificaciones,name='ver_notificaciones'),
] 
 No newline at end of file
+68 −0
Original line number Diff line number Diff line
@@ -12,6 +12,10 @@ from clase.models import Clase, VistaClase
from clase.forms import ClaseForm
from django.db.models import Count

from usuario.functions import enviar_correo_notificacion
from notificacion.forms import NotificacionForm
from notificacion.models import Notificacion
from django.urls import reverse



@@ -99,6 +103,7 @@ def get_curso(request, curso_id):
            'etiquetas':etiquetas,
            'num_clases': clases.count(),
            'num_examen': examenes.count(),
            'form_notificacion': NotificacionForm()
        }
        return render(request, 'curso/ver_curso_interno.html', contexto)

@@ -337,3 +342,66 @@ def alumnos_inscritos_curso(request, curso_id):
    }
    return render(request, 'curso/inscripciones.html',contexto)


@login_required
def enviar_notificacion(request,curso_id):
    if request.method == 'POST':

        form = NotificacionForm(request.POST)
        if form.is_valid(): # El asunto y la descripción estan correctos
            curso = get_object_or_404(Curso, pk=curso_id) # El curso al cual se enviara la notificación existe

            # Valido que la notificación que quiere enviar el instructor sea para un curso del cual el es el dueño
            curso_del_propietario = Curso.objects.filter( Q(id=curso_id) & Q(instructor=request.user)  )
            if not curso_del_propietario.exists()  : # El instructor no es dueño del curso
                return JsonResponse({'success': False, 'error': 'Acción no autorizada.' })
            
            # Extraigo los correos de los alumnos a los cuales se les enviara la notificación
            inscritos = Inscripcion.objects.filter( curso=curso )
            subject_del_correo = "Labsol - Notificación del curso: " + str( curso.nombre )
            recipient_list = [] 
            for inscrito in inscritos:
                recipient_list.append( inscrito.estudiante.email )

            # Se envia el correo a todos los inscritos al curso y guardamos en base de datos la notificación
            enviar_correo_notificacion(curso.nombre,recipient_list,request.POST.get('asunto'),descripcion=request.POST.get('descripcion'))
            nueva_notificacion = Notificacion( asunto=request.POST.get('asunto'), descripcion=request.POST.get('descripcion'), curso=curso, instructor=request.user )
            nueva_notificacion.save()

            URL_destino = reverse('curso:ver_curso', args=[curso_id])
            messages.success(request, 'Se ha enviado correctamente la notificacion') 
            return JsonResponse({'success':True,'redirectUrl': URL_destino}) 
        
        else:
            return JsonResponse({'success': False, 'errors': form.errors})
        
    else:
        return JsonResponse({'success': False, 'error': 'Método no autorizado.' })
    

# Muestra las notificaciones que ha enviado un instructor en un curso
@login_required
def ver_notificaciones(request,curso_id):
    curso = get_object_or_404(Curso, pk=curso_id) # El curso al cual se enviara la notificación existe

    # Valido que la notificaciones que quiere ver el instructor sean de un curso del cual el es el creador
    curso_del_propietario = Curso.objects.filter( Q(id=curso_id) & Q(instructor=request.user)  )
    print(curso_del_propietario)

    if not curso_del_propietario.exists()  : # El instructor no es dueño del curso
        return pagina_no_encontrada(request,"")

    # Extraigo todas las notificaciones creadas por el profesor ordenadas por fecha de la más reciente a la más antigua
    notificaciones = Notificacion.objects.filter(curso=curso).order_by('-fecha_creacion')
    context = {}
    context['curso'] = curso

    paginator = Paginator(notificaciones, 15)

    numero_de_pagina = request.GET.get('page')
    page_obj = paginator.get_page(numero_de_pagina)

    context = {}
    context['pagination'] = page_obj

    return render(request,'notificacion/notificaciones_instructor.html',context) 
+1 −1
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@
                        </div>
                    </div>
                    <div class="col-lg-6">
                        <div class="mb-3">
                        <div class="mbf-3">
                            <label for="respuesta2" class="form-label">Respuesta número 2:</label>
                            <input type="text" class="form-control" id="respuesta2" placeholder="Ingresa la respuesta 2" name="respuesta2" required>
                            <small class="form-hint">
+31 −0
Original line number Diff line number Diff line
<div class="modal modal-blur fade" id="confirmDeleteExamen" tabindex="-1" role="dialog" aria-labelledby="confirmDeleteModalLabel" aria-hidden="true">
  <div class="modal-dialog  modal-sm modal-dialog-centered" role="document">
    <div class="modal-content">
      <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
      <div class="modal-status bg-danger"></div>
      <div class="modal-body text-center py-4">
        <svg xmlns="http://www.w3.org/2000/svg" class="icon mb-2 text-danger icon-lg" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
          <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
          <path d="M12 9v2m0 4v.01" />
          <path d="M5 19h14a2 2 0 0 0 1.84 -2.75l-7.1 -12.25a2 2 0 0 0 -3.5 0l-7.1 12.25a2 2 0 0 0 1.75 2.75" />
        </svg>
        <h3 id="confirmDeleteModalLabel">¿Estás seguro de que deseas eliminar este examen?</h3>
        <div class="text-muted">Esta acción no se puede deshacer.</div>
      </div>
      <div class="modal-footer">
        <div class="w-100">
          <div class="row">
            <div class="col">
               <button type="button" class="btn btn-secondary w-100" data-bs-dismiss="modal">Cancelar</button>
            </div>
            <div class="col">
              <a id="deleteExamenBtn"  class="btn btn-danger w-100">Eliminar</a>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

+2 −0
Original line number Diff line number Diff line
@@ -13,4 +13,6 @@ urlpatterns = [
    path('agregar_pregunta/',views.crear_pregunta,name='crear_pregunta'),
    path('eliminar_pregunta/<int:pregunta_id>',views.eliminar_pregunta,name='eliminar_pregunta'),
    path('editar_pregunta/<int:pregunta_id>',views.editar_pregunta,name='editar_pregunta'),
    path('eliminar_examen/<int:examen_id>',views.eliminar_examen,name='eliminar_examen'),

]
+13 −3
Original line number Diff line number Diff line
@@ -8,8 +8,7 @@ from .functions import *
import random
from django.core.paginator import Paginator
from django.http import JsonResponse


from django.shortcuts import get_object_or_404


def pagina_no_encontrada(request, exception):
@@ -58,7 +57,6 @@ def crear_examen(request):
            return render(request, 'examen/crear_examen.html', {'examen': examen})
    return redirect('iniciar_sesion')

from django.shortcuts import get_object_or_404

def examen(request, examen_id):
    examen = Examen.objects.get(id=examen_id)
@@ -155,3 +153,15 @@ def editar_pregunta(request, pregunta_id):
    
    url = reverse('examen:detalles_examen', args=[pregunta.examen.id])
    return redirect(url)


def eliminar_examen(request, examen_id):
    examen = get_object_or_404(Examen, pk=examen_id)
    id = examen.curso.id
    try:
        examen.delete()
        messages.success(request,'Se ha eliminado el examen con éxito')
    except:
        messages.errors(request,'Hubo un error al eliminar el examen')
    url = reverse('curso:ver_curso', args=[id])
    return redirect(url)

notificacion/forms.py

0 → 100644
+13 −0
Original line number Diff line number Diff line
from django import forms
from .models import Notificacion

class NotificacionForm(forms.ModelForm):
    class Meta:
        model = Notificacion
        exclude = ['instructor','curso','fecha_creacion']

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['asunto'].widget.attrs.update({'class': 'form-control', 'placeholder':'Escribe el asunto de la notificación aquí'})
        self.fields['descripcion'].widget = forms.Textarea(attrs={'rows': 10, 'class': 'form-control', 'placeholder':'Escribe la descripción aquí'})
+8 −2
Original line number Diff line number Diff line
from django.db import models
from usuario.models import UsuarioPersonalizado
from curso.models import Curso
from django.utils import timezone


class Notificacion(models.Model):
    asunto = models.CharField(max_length=100)
    descripcion = models.TextField(max_length=150)
    url = models.CharField(max_length=250, null=True, blank=True)
    descripcion = models.TextField(max_length=250)
    instructor = models.ForeignKey(UsuarioPersonalizado, on_delete=models.CASCADE,null=True)
    curso = models.ForeignKey(Curso,on_delete=models.CASCADE,null=True)
    fecha_creacion = models.DateTimeField(default=timezone.now)

    def __str__(self):
        return self.asunto
 No newline at end of file
+37 −0
Original line number Diff line number Diff line
{% extends 'base.html' %} 
{% load static %}

{% block title %} Notificaciones del curso: {{ curso.nombre }} {% endblock title %}

{% block contenido %}
<div class="card p-0">
    <div class="table-responsive">
        <table class="table table-vcenter card-table">
            <thead>
                <tr>
                    <th>Fecha</th>
                    <th>Asunto</th>
                    <th>Descripción</th>
                </tr>
            </thead>
            <tbody>
                {% for notificacion in pagination %}
                <tr>
                    <td class="text-muted">{{ notificacion.fecha_creacion }}</td>
                    <td> {{ notificacion.asunto }} </td>
                    <td> {{ notificacion.descripcion }} </td>
                </tr>
                {% endfor %}
            </tbody>
        </table>
    </div>
</div>

{% endblock contenido %}


{% block pagination %}  {% include 'paginacion.html' %} {% endblock pagination %}

{% block js %} 

{% endblock js %}
+34 −0
Original line number Diff line number Diff line
<script>
    document.getElementById("form-notificacion").addEventListener("submit", function(event) {
    event.preventDefault();
    var formData = new FormData(event.target);
    fetch("{% url 'curso:enviar_notificacion' curso.id %}", {
      method: "POST",
      body: formData
    })
    .then(function(response) {
      return response.json();
    })
    .then(function(data) {
      if (data.success) {
        document.getElementById("notificacion").hidden = true;
        document.getElementById("form-notificacion").reset();
        location.reload()
      } else {
        // Mostramos errores relacionados al enviar una notificacion
        if(data.error){ 
          document.getElementById("div_error").hidden = false;
          document.getElementById("error").textContent = data.error
        }

        for (var field in data.errors) {
          var error = data.errors[field];
          var input = document.getElementById('id_notificacion_'+field);
          input.hidden = false;
          input.textContent = error;
        }
        
      }
    });
  });
  </script>
 No newline at end of file
+40 −99
Original line number Diff line number Diff line
html,body { height: 100%; }

    body{
    font-family: Arial, Helvetica, sans-serif;
}

/* Estilo para el contenedor principal */
.container {
        display: -ms-flexbox;
        display: -webkit-box;
        display: flex;
            justify-content: center; /* Centra horizontalmente */
            align-items: center; /* Centra verticalmente */
            min-height: 100vh; /* Asegura que ocupe al menos toda la altura de la ventana */
}

/* Estilo para el div que quieres centrar */
.centered-div {
    padding: 20px;
    border: 2px solid #2B5EF3;
    border-radius: 5px;
}

.login-block {
    width: 320px;
    padding: 20px;
    background: #fff;
    border-radius: 5px;
    border-top: 5px solid #2B5EF3;
    margin: 0 auto;
}

.login-block h1 {
    text-align: center;
    color: #000;
    font-size: 22px;
    margin-top: 0;
    margin-bottom: 20px;
        -ms-flex-align: center;
        -ms-flex-pack: center;
        -webkit-box-align: center;
        align-items: center;
        -webkit-box-pack: center;
        justify-content: center;
    }

.login-block input {
    width: 100%;
    height: 42px;
    box-sizing: border-box;
    border-radius: 5px;
    border: 1px solid #ccc;
    margin-bottom: 20px;
    form{
        padding-top: 0px;
        font-size: 14px;
    padding: 0 20px 0 50px;
    outline: none;
        margin-top: 20px;
    }

.login-block input#username {
    background: #2B5EF3 ;
    background-size: 16px 80px;
}
    .card-title{ font-weight:300; }

.login-block input#username:focus {
    background: #2B5EF3;
    background-size: 16px 80px;
    .btn{
        font-size: 14px;
        margin-top:20px;
    }

.login-block input:active, .login-block input:focus {
    border: 1px solid #2B5EF3;
    .login-form{ 
        width:320px;
        margin:20px;
    }

/* Botón de enviar */
.login-block button {
    width: 100%;
    height: 40px;
    background: #2B5EF3;
    box-sizing: border-box;
    border-radius: 5px;
    border: 1px solid #2B5EF3;
    color: #fff;
    font-weight: bold;
    text-transform: uppercase;
    font-size: 14px;
    outline: none;
    cursor: pointer;
    .sign-up{
        text-align:center;
        padding:20px 0 0;
    }


.login-block a{
    display: block; 
    background-color: red; 
    margin-top: 5px; 
    padding: 10px 38%;
    text-decoration: none;
    color: white;
    border-radius: 5px;
    span{
        font-size:14px;
    font-weight: bold;
    text-transform: uppercase;
}


.errors{
    color: red;
    font-size: small;
    }
 No newline at end of file
+12 −8
Original line number Diff line number Diff line
function buscarCursos(idUsuario) {
  const searchCurso = document.getElementById('buscador-cursos');
  const cardCurso = document.getElementById('card_display');
  const cursosBuscados = document.getElementById('display_search_buscador');
  const desplegar_buscador=document.getElementById('desplegar_buscador')
  cardCurso.style.flexWrap = 'wrap';
  
  cardCurso.style.flexWrap = 'wrap';
  let cursosUsuario = [];
  let searchArreglo=[]
  fetch('/api/solicitudCursos_api/')
    .then((response) => response.json())
    .then((data) => {
      searchArreglo = data;
      
    })

  fetch('/api/curso_api/')
@@ -21,16 +20,21 @@ function buscarCursos(idUsuario) {
      return response.json();
    })
    .then(data => {
      
      if (idUsuario === 'admin') {
          cursosUsuario = data;
      } else {
        cursosUsuario = data.filter(curso => curso.instructor === idUsuario);
          
      }
        cursosInstructor=cursosUsuario.filter((dataResult)=>{
          return !searchArreglo.some(
            
            (searchResult)=> searchResult.curso === dataResult.id
          )
        })

       
        searchCurso.addEventListener('input', () => {
          const textoBusqueda = searchCurso.value.toLowerCase();
          const desplegarBuscador = document.getElementById('desplegar_info');
+7 −0
Original line number Diff line number Diff line
input_email = document.getElementById('id_email');
input_email.classList.add("form-control");
input_email.classList.add("form-control-sm");
input_email.placeholder  = "Ingresa tu correo electrónico";

error_p = document.getElementById("errors");
error_p.classList.add('text-danger');
+13 −0
Original line number Diff line number Diff line
input_password1 = document.getElementById('id_new_password1');
input_password1.classList.add("form-control");
input_password1.classList.add("form-control-sm");
input_password1.classList.add("mb-2");
input_password1.placeholder = "Escribe la nueva contraseña";

input_password2 = document.getElementById('id_new_password2');
input_password2.classList.add("form-control");
input_password2.classList.add("form-control-sm");
input_password2.placeholder = "Confirma la nueva contraseña";

error_p = document.getElementById("errors");
error_p.classList.add('text-danger');
 No newline at end of file
+2 −0
Original line number Diff line number Diff line
@@ -160,6 +160,8 @@
      </div>
    </div>
    {% block js %} 
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iK7l5iS9omQjs4uPLF5ceMSR66nHD7L9OpFAYQOp6mymP64pVy" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-eKpEn3Z3aOQVMpG8r+3doD4W1Nw5okAEl7WlC/7A2oRj5/jqDLOdM6D5OJUT8ii1" crossorigin="anonymous"></script>
    {% endblock js %}
    <script src="{% static 'js/tabler.min.js' %}" defer></script>
  </body>
+31 −19
Original line number Diff line number Diff line
@@ -78,6 +78,17 @@
                    <form action="{% url 'buscar' %}" method="get" class="me-3 py-3">
                        <input type="search" value="{{ query }}" class="buscador form-control" placeholder="Buscar" aria-label="Search in website" name="q" id="search-input">
                    </form>
                    {% if curso %}
                        <a href="{% url 'iniciar_sesion' %}" class="nav-item btn btn-primary py-4 px-lg-5 d-lg-block">
                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-app-window" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
                                <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
                                <path d="M3 5m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z" />
                                <path d="M6 8h.01" />
                                <path d="M9 8h.01" />
                            </svg>
                            Regresar a la página inicial
                        </a>
                    {% else %}
                        <a class="nav-item nav-link active" style="cursor: pointer;" data-bs-toggle="modal" data-bs-target="#registro">
                            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-user-plus" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
                                <path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
@@ -97,6 +108,7 @@
                            </svg>
                            Iniciar sesión
                        </a>
                    {% endif %}
                    <div id="search-results" class="search-results-container" style="display: none;"></div>
                {% endif %}
            </div>
+37 −0
Original line number Diff line number Diff line
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
<title>Labsol Academy</title>

</head>
<!-- body -->
<body>
    <div style="width: 100%; float: left; background-color: #2F3659;
    height: auto; padding-top: 20px; padding-bottom: 16px; background-size: cover;">
        <div style="width: 100%; float: left; margin: 0 auto; text-align: center;">
            <h1 style="font-size: 46pt; color: #ffffff; padding-top: 10px; font-family: 'Raleway', sans-serif;">Notificación de<br>
            <span style="color: #10b5fa">LABSOL ACADEMY</span></h1>
        </div>
    </div>
<!--banner end -->
    <!--services start -->
    <div class="">
        <div style="width: 100%; padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; max-width: 540px; min-width: 992px;">
            <div style="width: 100%; float: left; background-color: #ffffff; height: auto; padding-bottom: 30px;padding-top: 30px; box-shadow: 0px 0px 10px 0px; color: #e7e7e7; margin-top: -110px; margin-bottom: 50px;">
                <h1 style="color: #050000; font-size: 22pt;border-bottom: 1px solid #000; width: 46%; margin: 0 auto; text-align: center; font-weight: bold; letter-spacing: 0; position: relative; padding: 0 0 10px 0; line-height: normal; font-family: inherit; box-sizing: border-box !important;transition: ease all 0.5s;">{{ asunto }}</h1>
                <p style="color: #0b2855; text-align: justify; margin: 20px; font-weight: 400; font-size: 18px;line-height: 24px;">
                    {{ descripcion }}
                </p>

                <p style="color: #0d2e1f; text-align: justify; margin: 20px; font-weight: 400; font-size: 12px;line-height: 24px;">
                    Esta notificación te ha llegado porque estas inscrito al curso {{ nombre_del_curso }} de Labsol Academy.
                </p>
            </div>
        </div>
    </div>		    
</body>
</html>
 No newline at end of file
+4 −0
Original line number Diff line number Diff line
@@ -91,7 +91,11 @@
      </svg>
    </span>
    <span class="nav-link-title">
<<<<<<< HEAD
      Videollamadas/Conferencias
=======
      Videollamadas
>>>>>>> 2454dc21ec812d67c571a977307cc268500e5559
    </span>
  </a>
</li>
 No newline at end of file
+1 −1
Original line number Diff line number Diff line
@@ -108,7 +108,7 @@
      </svg>
    </span>
    <span class="nav-link-title">
      Videollamadas/Conferencias
      Videollamadas
    </span>
  </a>
</li>
 No newline at end of file
+22 −6
Original line number Diff line number Diff line
@@ -8,6 +8,10 @@ from django.conf import settings
from django.contrib import messages
from django.http import JsonResponse
from django.urls import reverse
from django.core.mail import send_mail, EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.conf import settings

def procesar_registro_interno(usuario,request):
    #enviar_correo_registro(usuario)
@@ -38,10 +42,6 @@ def procesar_registro_externo(usuario):
#    
#    send_mail(subject, message, from_email, recipient_list)

from django.core.mail import send_mail, EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.conf import settings

def enviar_correo_aceptacion(usuario):
    subject = 'Bienvenido a Labsol Academy'
@@ -61,6 +61,22 @@ def enviar_correo_aceptacion(usuario):
    # Envía el correo
    email.send()

def enviar_correo_notificacion(nombre_del_curso, recipient_list,asunto,descripcion):
    subject = "Labsol Academy - Notificación del curso " + str( nombre_del_curso )
    from_email = settings.EMAIL_HOST_USER

    # Renderiza el contenido del correo en HTML usando una plantilla
    html_message = render_to_string('notificaciones_curso.html', {'nombre_del_curso':nombre_del_curso,'asunto':asunto,'descripcion':descripcion})

    # Obtiene el contenido de texto plano a partir del contenido HTML
    text_message = strip_tags(html_message)

    # Crea una instancia de EmailMultiAlternatives para enviar contenido HTML y de texto plano
    email = EmailMultiAlternatives(subject, text_message, from_email, recipient_list)
    email.attach_alternative(html_message, "text/html")  # Adjunta el contenido HTML

    # Envía el correo
    email.send()

def generar_solicitud(usuario):
    solicitud = Solicitud(usuario=usuario)
+0 −1
Original line number Diff line number Diff line

{% extends 'base.html' %} 
{% load static %}

+5 −1
Original line number Diff line number Diff line
@@ -35,7 +35,11 @@
            <div class="col-lg-6">
              <div class="mb-3">
                <label class="form-label">Selecciona el tipo de usuario</label>
                {{ forms.user_type }}
                <select name="user_type" class="form-control" required id="id_user_type">
                  <option value="" selected>Seleccione una opción</option>
                  <option value="instructor">Instructor</option>
                  <option value="estudiante">Estudiante</option>
                </select>
              </div>
            </div>
          </div>
+30 −17
Original line number Diff line number Diff line
@@ -5,37 +5,50 @@
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Labsol Academy - Restablecer contraseña</title>
    <link href="{% static 'css/bootstrap.min.css'  %}" rel="stylesheet">
    <link href="{% static 'css/restablecer.passwd.css'  %}" rel="stylesheet">
    
</head>


<body>
    
    <!-- Formulario de ¿Has olvidado tu contraseña -->
    <div class="container">
        <div class="centered-div" style="border: none;">
            <div class="login-block" style="border: none; margin: 0; padding: 0;">
                <h1>¿Has olvidado tu contraseña?</h1>
                <form class="" method="post">
                    {% csrf_token %}

    <div class="card login-form">
        <div class="card-body">
            <h3 class="card-title text-center">Restablecer contraseña</h3>
            
            <div class="card-text">
                <form method="post">
                    {% csrf_token %}
                    <div class="form-group text-center">
                        <label for="exampleInputEmail1">Ingrese su dirección de correo electrónico y le enviaremos un enlace para restablecer su contraseña.</label>
                        {% for field in form %}
                            {{ field.label_tag }} 
                    
                            {{ field }}

                            {% if field.errors %}
                                <small>{{ field.errors|striptags  }}</small> 
                                <small id="errors">{{ field.errors|striptags  }}</small> 
                            {% endif %}
                        {% endfor %}              

                        {% endfor %}
                    </div>

                    <button type="submit" style="background-color: #052666; border-radius: 5px;">Enviar</button>
                    <a href="{% url  'iniciar_sesion' %}" style="background-color: #EC3510; border-radius: 1px #EC3510 5px;">Cancelar</a>
                    <div class="container p-0">
                        <div class="row">
                          <div class="col-12 text-center">
                            <button type="submit" style="width: 100%;" class="btn btn-primary">Restablecer contraseña</button>
                            <a href="{% url  'iniciar_sesion' %}" style="width: 100%;" class="btn btn-danger">Cancelar</a>
                          </div>
                        </div>
                    </div>
    
                </form>
            </div>
        </div>
    </div>
    <!-- fin del formulario de ¿Has olvidado tu contraseña -->

    <script src="{% static 'js/reset_passwd.js' %}"></script>
    
</body>
</html>
 No newline at end of file
+4 −6
Original line number Diff line number Diff line
@@ -9,13 +9,11 @@
    <link href="{% static 'css/restablecer.passwd.css'  %}" rel="stylesheet">
</head>
<body>
    
    <div class="container">
        <div class="centered-div" style="text-align: center;">
            <h2>Tu contraseña ha sido actualizada exitosamente</h2>
            <p>Da Click <a href="{% url 'iniciar_sesion' %}">aquí</a> para ir a la página de inicio.</p>
    <div class="container text-center">
        <div  role="alert">
            <h1>Tu contraseña ha sido actualizada exitosamente</h1>
            <p style="font-size: 14px;">Da Click <a href="{% url 'iniciar_sesion' %}">aquí</a> para ir a la página de inicio.</p>
        </div>
    </div>

</body>
</html>
 No newline at end of file
+29 −17
Original line number Diff line number Diff line
@@ -4,35 +4,47 @@
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="{% static 'css/restablecer.passwd.css'  %}" rel="stylesheet">
    <link href="{% static 'css/bootstrap.min.css'  %}" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'css/restablecer.passwd.css' %}">
    <title>Labsol Academy - Restablecer contraseña</title>
</head>
<body>

  <div class="container">
    <div class="centered-div" style="border: none;">
        <div class="login-block" style="border: 0; padding: 0;">
            <h1>Crea una nueva contraseña</h1>
<div class="card login-form">
  <div class="card-body">
      <h3 class="card-title text-center">Crea una nueva contraseña</h3>
      
      <div class="card-text">
          <form method="post">
              {% csrf_token %}
              <div class="form-group text-center">
                  <label for="exampleInputEmail1">Escribe la nueva contraseña con la que ingresaras al sistema.</label>
                  
                  {% for field in form %}
                        {{ field.label_tag }} 
                        {{ field }}
                        {% if field.errors %}
                            <small class="errors">{{ field.errors|striptags  }}</small> 
                            <small class="errors" id="errors">{{ field.errors|striptags  }}</small> 
                        {% endif %}
                {% endfor %}

              </div>

              <div>
                <button type="submit" style="background-color: #052666; border-radius: 5px;" >Restablecer contraseña</button>
              <div class="container p-0">
                  <div class="row">
                    <div class="col-12 text-center">
                      <button type="submit" style="width: 100%;" class="btn btn-primary">Restablecer contraseña</button>
                    </div>
                  </div>
              </div>

          </form>
      </div>
  </div>
</div>


<script src="{% static 'js/reset_passwd_confirm.js' %}"></script>

        
</body>
</html>
 No newline at end of file
+12 −4
Original line number Diff line number Diff line
<p>Parece que ha olvidado su contraseña para ingresar a LABSOL Academy. Si es así, haga clic en el siguiente enlace para restablecer su contraseña.
</p>

{{ protocol }}://{{ domain }}{% url "password_reset_confirm" uidb64=uid token=token %}
{% load i18n %}{% autoescape off %}

    Hola {{ user.username }},

    Has solicitado restablecer tu contraseña. Por favor, sigue el siguiente enlace para cambiar tu contraseña:

    {{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}

    Si no solicitaste restablecer tu contraseña, por favor ignora este correo electrónico.

    Gracias, El equipo de Labsol Academy.
{% endautoescape %}
               
 No newline at end of file
<p> <strong>Si no ha olvidado su contraseña, haga caso omiso de este correo electrónico.</strong></p>
+2 −0
Original line number Diff line number Diff line
@@ -17,5 +17,7 @@ urlpatterns = [
	path('editar/<int:usuario_id>/', views.editar_usuario, name='editar_usuario'),
	path('solicitud/<int:solicitud_id>/<str:argumento>/', views.procesar_solicitud, name='procesar_solicitud'),
	path('eliminar/<int:usuario_id>/', views.eliminar_usuario, name='eliminar_usuario'),
    path('procesar_registro_instructor/', views.procesar_registro_instructor, name='procesar_registro_instructor'),

]
+18 −2
Original line number Diff line number Diff line
@@ -3,16 +3,25 @@ from django.contrib.auth import (authenticate, login, logout,
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.views import LoginView
from django.http import JsonResponse
from django.shortcuts import render
from django.shortcuts import get_object_or_404, render
from django.views.decorators.http import require_POST

from curso.models import Categoria, Curso

from .forms import FormUpdateUsuario, FormUsuario
from .functions import *
from .models import UsuarioPersonalizado
from django.contrib.auth.views import PasswordResetView
from django.urls import reverse_lazy


from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.contrib.auth.tokens import default_token_generator

# Clase que define la ruta del template personalizado del correo electrónico, el cual contiene el enlace para restablecer la contraseña. 
class CustomPasswordResetView(PasswordResetView):
    email_template_name = 'usuario/passwordReset/password_reset_email.html'
    success_url = reverse_lazy('password_reset_done')

def pagina_no_encontrada(request, exception):
    return render(request, '404_template.html', status=404)
@@ -110,8 +119,13 @@ def get_solicitud(request):
    page_obj = paginator.get_page(page_number)
    return render(request, 'usuario/solicitud/solicitud_list.html', {"pagination": page_obj})


@login_required
@user_passes_test(lambda user: user.user_type == 'admin' or user.is_superuser)
def procesar_registro_instructor(request):
    pass

@login_required
@user_passes_test(lambda user: user.user_type == 'admin' or user.is_superuser)
def procesar_solicitud(request, solicitud_id, argumento):
    solicitud = Solicitud.objects.get(id=solicitud_id)
@@ -126,6 +140,8 @@ def procesar_solicitud(request, solicitud_id, argumento):
    solicitud.delete()
    return redirect('usuario:lista_solicitud')



@login_required
@user_passes_test(lambda user: user.user_type in ['instructor','admin', 'estudiante'])
def perfil(request):
+21 −2
Original line number Diff line number Diff line
@@ -31,7 +31,26 @@ class FormVideollamada(forms.ModelForm):
        self.fields['archivo'].required = False
        self.fields['hora'].required = True
        self.fields['instructor'].required = True
        self.fields['miniatura'].required = False
        self.fields['miniatura'].required = True
        

class FormVideollamadaEditar(forms.ModelForm):
    class Meta:
        model = Videollamada
        fields = ['titulo', 'descripcion', 'enlace', 'fecha_reunion', 'archivo', 'hora', 'instructor', 'miniatura', 'video']
        widgets = {
            'titulo': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Ingrese el título de la conferencia'}),
            'enlace': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Ingrese la URL'}),
            'archivo': forms.FileInput(attrs={'class': 'form-control'}),
            'hora': forms.TimeInput(attrs={'type': 'time', 'class':'form-control'}),
            'fecha_reunion':  forms.TextInput(attrs={'class': 'form-control', 'type':'date'}),
            'instructor': forms.Select(attrs={'class': 'form-select'}),
            'descripcion': forms.Textarea(attrs={'rows': 0, 'cols': 10, 'class': 'form-control'}),
            'miniatura': forms.FileInput(attrs={'class': 'form-control'}),
            'video': forms.FileInput(attrs={'class': 'form-control'})  # Agregado el widget para el campo 'video'
        }

    def __init__(self, *args, **kwargs):
        super(FormVideollamadaEditar, self).__init__(*args, **kwargs)
        self.fields['video'].required = True
        # Resto del código para configurar campos requeridos u otras personalizaciones específicas para la edición
+19 −2
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
{% load static %}

{% block contenido %}
{% if pagination %}
<div class="container-xxl py-5">
    <div class="container">
        <div class="text-center wow fadeInUp" data-wow-delay="0.1s">
@@ -13,7 +14,7 @@
            <div class="col-lg-4 col-md-6 wow fadeInUp" data-wow-delay="0.1s">
                <div class="course-item bg-light">
                    <div class="overflow-hidden">
                        <img class="img-fluid " style=" width: 100%; height: 300px  "  src="{{ videollamadas.miniatura.url }}" alt="Foto de perfil de instructor">
                        <img class="img-fluid " style=" width: 100%; height: 300px  "  src="{{ videollamadas.miniatura.url }}" alt="miniatura de conferencia">
                    </div>
                    <div class="text-center p-4 pb-0">
                        <h5 class="mb-4" style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 400px;">
@@ -44,4 +45,20 @@
        </div>
    </div>
</div>
{% else %}
<div id="class-message" data-class="0" class="container-xxl py-5 wow fadeInUp" data-wow-delay="0.1s">
    <div class="container text-center">
        <div class="row justify-content-center">
            <div class="col-lg-6">
                <i class="bi bi-emoji-frown-fill display-1 text-primary"></i>
                <h1 class="mb-4">No hay conferencias disponibles por el momento</h1>
            </div>
        </div>
    </div>
</div>
{% endif%}

{% endblock contenido %}   


{% block pag %} {% if pagination %} {% include 'paginacion.html' %} {% endif %} {% endblock pag%}
+9 −3
Original line number Diff line number Diff line
@@ -40,6 +40,12 @@
              </div>
            </div>
          </div>
          {% if videollamada %}
            <div class="mb-3">
              <label class="form-label">Video:</label>
              {{ forms.video }}
            </div>
          {% endif %}
          <div class="row">
            <div class="col-lg-6">
              <div class="mb-3">
+3 −3
Original line number Diff line number Diff line
@@ -72,7 +72,7 @@ def subir_video(request):
def ver_videollamada(request, videollamada_id):
    videollamada = Videollamada.objects.get(id=videollamada_id)
    if request.user.user_type!='estudiante':
        form = FormVideollamada(instance=videollamada)
        form = FormVideollamadaEditar(instance=videollamada)
        form.fields['fecha_reunion'].input_formats = ['%d/%m/%Y']
        contexto = {
        'videollamada':videollamada,
@@ -90,13 +90,13 @@ def editar_video(request):
    id_videollamada = request.POST.get('videollamada_id')
    videollamada = Videollamada.objects.get(id=id_videollamada)
    if request.method == 'POST':
        form = FormVideollamada(request.POST, request.FILES, instance=videollamada)
        form = FormVideollamadaEditar(request.POST, request.FILES, instance=videollamada)
        if form.is_valid():
            form.save()
            messages.success(request, 'Se ha editado la videollamada correctamente')
        else:
            messages.error(request, form.errors)
    else:
        form = FormVideollamada(instance=videollamada)
        form = FormVideollamadaEditar(instance=videollamada)
    url = reverse('videollamada:detalle_video', args=[id_videollamada])
    return redirect(url)
 No newline at end of file

videos/__init__.py

deleted100644 → 0
+0 −0

Empty file deleted.

−183 B

File deleted.

−161 B

File deleted.

−238 B

File deleted.

−202 B

File deleted.

−554 B

File deleted.

−438 B

File deleted.

−235 B

File deleted.

−199 B

File deleted.

−434 B

File deleted.

−331 B

File deleted.

−1.43 KiB

File deleted.

−779 B

File deleted.

videos/admin.py

deleted100644 → 0
+0 −3
Original line number Diff line number Diff line
from django.contrib import admin

# Register your models here.

videos/apps.py

deleted100644 → 0
+0 −6
Original line number Diff line number Diff line
from django.apps import AppConfig


class VideosConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "videos"

videos/migrations/__init__.py

deleted100644 → 0
+0 −0

Empty file deleted.

−194 B

File deleted.

−172 B

File deleted.

videos/models.py

deleted100644 → 0
+0 −3
Original line number Diff line number Diff line
from django.db import models

# Create your models here.

videos/tests.py

deleted100644 → 0
+0 −3
Original line number Diff line number Diff line
from django.test import TestCase

# Create your tests here.

videos/urls.py

deleted100644 → 0
+0 −8
Original line number Diff line number Diff line
from django.urls import path
from videos import views

app_name = 'video'

urlpatterns = [
    path('subir_video/', views.subir_video, name='subir_video'),
]

videos/views.py

deleted100644 → 0
+0 −25
Original line number Diff line number Diff line
from django.shortcuts import render
from django.http import JsonResponse
import os

def subir_video(request):
    if request.method == 'POST':
        archivo = request.FILES['video']
        video_path = 'media/videos'  # Establecer la ruta directamente a media/videos

        # Asegúrate de que el directorio exista
        os.makedirs(video_path, exist_ok=True)

        # Guardar el video en el directorio
        with open(os.path.join(video_path, archivo.name), 'wb+') as destination:
            for chunk in archivo.chunks():
                destination.write(chunk)

        # Generar una URL única o identificador (puede ser el nombre del archivo en este caso)
        video_url = f'http://http://localhost:8000/media/video/{archivo.name}'
        print(request.POST)

        return JsonResponse({'video_url':video_url}) 
    else:
        return JsonResponse({'method': 'get'})
+1 −1

File changed.

Contains only whitespace changes.