<?php
namespace App\Service;
use App\Entity\Alumno;
use App\Entity\Curso;
use App\Entity\TareaCurso;
use App\Entity\TareaCursoPlantilla;
use Doctrine\ORM\EntityManagerInterface;
use Mpdf\Mpdf;
use Mpdf\MpdfException;
use Mpdf\Output\Destination;
use Symfony\Component\Filesystem\Filesystem;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
class CursoService
{
public function __construct(
private readonly EntityManagerInterface $em,
private readonly Environment $environment
)
{
}
// Esta función se usa para leer las tareas plantilla que hay creadas en la config. global y añadirselas a un curso
// al crearlo.
public function anadirTareasPlantilla(Curso $curso): void
{
$tareasPlantilla = $this->em->getRepository(TareaCursoPlantilla::class)->findBy([
'visible' => true,
'borrado' => false
]);
foreach ($tareasPlantilla as $tareaPlantilla) {
$tarea = new TareaCurso();
$tarea
->setNombre($tareaPlantilla->getNombre())
->setDescripcion($tareaPlantilla->getDescripcion());
$this->em->persist($tarea);
$curso->addTarea($tarea);
}
}
// Esta función se llama cada vez que se actualiza un curso y es para realizar unos ajustes en unos campos que son
// autocalculados.
public function ajustarCamposAutocalculados(Curso $curso): void
{
// Para actualizar las fechas de seguimiento de los alumnos según la "fecha de firma acta selección".
if ($curso->getFechaFirmaActaSeleccion()) {
$fechaSeguimientoAlumnoMes = (clone $curso->getFechaFirmaActaSeleccion())->modify('+1 months');
$fechaSeguimientoAlumnoSeisMeses = (clone $curso->getFechaFirmaActaSeleccion())->modify('+6 months');
$alumnos = $this->em->getRepository(Curso::class)->obtenerAlumnos($curso);
/** @var Alumno $a */
foreach ($alumnos as $a) {
// Si el estado es "Persona formada", se le actualizan las fechas de seguimiento.
if ($a->getEstado()->getId() === 4) {
$a
->setFechaLlamadaMes($fechaSeguimientoAlumnoMes)
->setFechaLlamadaSeisMeses($fechaSeguimientoAlumnoSeisMeses);
}
}
}
// Para actualizar los campos de duración en horas y en días del curso.
$horasFormacionEspecificaEnSeg = $curso->getHorasFormacionEspecifica() ?? 0;
$horasFormacionPracticaProfesionalEnSeg = $curso->getHorasPracticaProfesional() ?? 0;
$horasFormacionTransversalEnSeg = $curso->getHorasFormacionTransversal() ?? 0;
$horasFormacionComplementariaEnSeg = $curso->getHorasFormacionComplementaria() ?? 0;
$horasTutoriaEspecificaEnSeg = $curso->getHorasTutoriaEspecifica() ?? 0;
$horasTutoriaPracticaEnSeg = $curso->getHorasTutoriaPractica() ?? 0;
$totalHorasEnSegundos =
$horasFormacionEspecificaEnSeg +
$horasFormacionPracticaProfesionalEnSeg +
$horasFormacionTransversalEnSeg +
$horasFormacionComplementariaEnSeg +
$horasTutoriaEspecificaEnSeg +
$horasTutoriaPracticaEnSeg;
$curso
->setHorasDuracion($totalHorasEnSegundos)
->setDiasDuracion($totalHorasEnSegundos);
// Para actualizar el campo de ayudaMaxima (Beca).
// El 5 es porque son 5 horas por día.
$curso->setAyudaMaxima((int)($curso->getDiasDuracion()/60/60/5) * $curso->getCosteDiaAsistencia());
}
// Esta función se utiliza en la vista detalle del curso, en la pestaña de tareas.
public function datosBarraProgresoTareas(Curso $curso): array
{
$result = [
'totalTareas' => $curso->getTareas()->count(),
'totalTareasCompletadas' => 0,
'porcentajeCompletadas' => 0
];
if ($result['totalTareas'] > 0) {
foreach ($curso->getTareas() as $tarea) {
if ($tarea->isCompletada()) {
$result['totalTareasCompletadas'] += 1;
}
}
$result['porcentajeCompletadas'] = number_format((($result['totalTareasCompletadas'] * 100) / $result['totalTareas']), 2);
}
return $result;
}
// Función que se usa en la función "estadisticaTestEvaluacion".
private function getEstructuraPreguntasBase(): array
{
// 🔹 Plantillas reutilizables
$respuestasMedios = [
'Prensa' => 0,
'Carteles informativos' => 0,
'Página web' => 0,
'Redes sociales' => 0,
'Ayuntamiento' => 0,
'Servicios Sociales Comunitarios' => 0,
'Asociaciones' => 0,
'Otros' => 0
];
$respuestasSiNo = [
'Sí' => 0,
'No' => 0
];
$respuestasCuatroNiveles = [
'Mucho' => 0,
'Suficiente' => 0,
'Poco' => 0,
'Nada' => 0
];
$respuestasEscala10 = array_fill_keys(range(1, 10), 0);
$respuestasUtilidad = [
'Muy útil y aplicable a mi vida laboral o personal' => 0,
'Útil, aunque me hubiera gustado profundizar más' => 0,
'Algo básico o repetitivo, esperaba más contenido nuevo' => 0,
'Poco útil, no se relacionaba con mis intereses o necesidades' => 0
];
// 🔹 Ahora definimos todas las preguntas usando las plantillas
return [
'medioInformante' => [
'tituloPregunta' => '¿A través de qué medio recibiste la información sobre Proempleo8 y sus cursos de formación?',
'respuestas' => $respuestasMedios,
'totalRespuestas' => 0
],
'medioMasEfectivo' => [
'tituloPregunta' => '¿Qué medio consideras que fue el más efectivo para informarte sobre los cursos?',
'respuestas' => $respuestasMedios,
'totalRespuestas' => 0
],
'considerasDifusionAdecuada' => [
'tituloPregunta' => '¿Consideras que la difusión del curso ha sido adecuada?',
'respuestas' => [
'Sí, fue muy adecuada y me permitió acceder fácilmente a la información' => 0,
'Regular, aunque podría mejorar en algunos aspectos' => 0,
'No, considero que la difusión fue insuficiente o poco efectiva' => 0,
'No estoy seguro/a' => 0,
],
'totalRespuestas' => 0
],
'accesoUtilizacionWeb' => [
'tituloPregunta' => 'El acceso y utilización de la página web Proempleo8 me resultó',
'respuestas' => [
'Muy fácil y claro, no tuve ningún problema' => 0,
'Normal, aunque con algunos detalles mejorables' => 0,
'Algo complicado, me costó adaptarme' => 0,
],
'totalRespuestas' => 0
],
'cargaErrores' => [
'tituloPregunta' => '¿La página web Proempleo8 cargaba rápidamente y sin errores?',
'respuestas' => [
'Sí, siempre' => 0,
'A veces tardaba un poco' => 0,
'Con frecuencia era lenta o daba errores' => 0,
],
'totalRespuestas' => 0
],
'recomendacionMejoras' => [
'tituloPregunta' => '¿Recomendarías mejorar algún aspecto técnico o de usabilidad de la página web Proempleo8?',
'respuestas' => [
'No, todo funcionó correctamente' => 0,
'Sí' => 0
],
'totalRespuestas' => 0
],
'experienciaAcceso' => [
'tituloPregunta' => '¿Cómo fue tu experiencia al rellenar el cuestionario inicial para acceder al curso del Proyecto Proempleo8?',
'respuestas' => [
'Muy buena, fue claro y fácil de completar' => 0,
'Regular, me costó entender algunas preguntas' => 0,
'Mala, tuve dificultades para completarlo y necesité ayuda' => 0
],
'totalRespuestas' => 0
],
'dificultadEnvioSolicitudSede' => [
'tituloPregunta' => '¿Tuviste alguna dificultad para enviar la solicitud por sede electrónica o cualquiera de las demás opciones?',
'respuestas' => [
'No, el proceso fue fácil y sin inconvenientes' => 0,
'Sí, tuve dificultades' => 0
],
'totalRespuestas' => 0
],
'opinionFormacionEspecifica' => [
'tituloPregunta' => 'La formación específica me pareció',
'respuestas' => $respuestasUtilidad,
'totalRespuestas' => 0
],
'opinionFormacionTransversal' => [
'tituloPregunta' => 'Los módulos transversales me parecieron',
'respuestas' => $respuestasUtilidad,
'totalRespuestas' => 0
],
'opinionFormacionComplementaria' => [
'tituloPregunta' => 'La formación complementaria me pareció',
'respuestas' => $respuestasUtilidad,
'totalRespuestas' => 0
],
'valoracionTeoricoCurso' => [
'tituloPregunta' => 'En general, ¿cómo valorarías el contenido teórico del curso en una escala del 1 al 10?',
'respuestas' => $respuestasEscala10,
'totalRespuestas' => 0
],
'cumplidoExpectativasInicioPracticas' => [
'tituloPregunta' => 'Se han cumplido las expectativas marcadas al inicio de las prácticas',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'horasDedicadasPartePractica' => [
'tituloPregunta' => 'Las horas dedicadas a la parte práctica del curso me han parecido',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'formacionTeoricaAyudaPracticas' => [
'tituloPregunta' => 'La formación teórica me ha ayudado para la realización de las prácticas',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'incidenciasResueltasTutor' => [
'tituloPregunta' => 'Las incidencias surgidas han sido resueltas por el tutor con eficacia',
'respuestas' => [
'Sí, todas' => 0,
'Algunas' => 0,
'No, pocas' => 0
],
'totalRespuestas' => 0
],
'tareasAdecuadasFinalidadCurso' => [
'tituloPregunta' => 'Las tareas realizadas han sido las adecuadas a la finalidad del curso',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'empresaFacilitaIntegracion' => [
'tituloPregunta' => 'La empresa se ha implicado facilitándome ayuda para que me integrara mejor',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'autorealizadoConTareas' => [
'tituloPregunta' => 'Me he sentido útil en el desarrollo de las tareas realizadas',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'aprendizajeSuficienteParaTrabajar' => [
'tituloPregunta' => 'Considero que el aprendizaje ha sido suficiente para incorporarme a trabajar',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'valoracionPracticasCurso' => [
'tituloPregunta' => 'En general, ¿cómo valorarías las prácticas del curso en una escala del 1 al 10?',
'respuestas' => $respuestasEscala10,
'totalRespuestas' => 0
],
'contarConEmpresaFuturosCursos' => [
'tituloPregunta' => '¿Contarías con esta empresa para realizar las prácticas en futuros cursos?',
'respuestas' => $respuestasSiNo,
'totalRespuestas' => 0
],
'tutoraProporcionoOrientacionRealizacionCurso' => [
'tituloPregunta' => '¿La tutora te proporcionó orientación clara sobre la realización del curso?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'tutoraDisponibleDudasCurso' => [
'tituloPregunta' => '¿La tutora estuvo disponible para resolver las dudas de forma clara y efectiva durante el curso?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'profesorAcompaniaEnTeoria' => [
'tituloPregunta' => '¿Te sentiste acompañado/a por el tutor durante el desarrollo de la teoría?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'tutorMuestraInteresEvolucionAlumno' => [
'tituloPregunta' => '¿El tutor mostró interés por tu evolución y aprendizaje?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'escuchadoEnTutorias' => [
'tituloPregunta' => '¿Te sentiste escuchado/a y comprendido/a durante las tutorías?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'tutoriasUtilesAprendizaje' => [
'tituloPregunta' => '¿Consideras que las tutorías han sido útiles para tu proceso de aprendizaje?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'valoracionTutoriasCurso' => [
'tituloPregunta' => 'En general, ¿cómo valorarías las tutorías hasta la fecha en una escala del 1 al 10?',
'respuestas' => $respuestasEscala10,
'totalRespuestas' => 0
],
'profesorExplicaBien' => [
'tituloPregunta' => '¿El profesor/a explicó los contenidos de manera clara y comprensible?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'profesorFomentaParticipacionGrupoSesionesTeoricas' => [
'tituloPregunta' => '¿Fomentó la participación del grupo durante las sesiones teóricas?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'profesorResuelveDudasBien' => [
'tituloPregunta' => '¿Resolvió tus dudas con claridad y paciencia?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'profesorServicialEnNecesidad' => [
'tituloPregunta' => '¿Se mostró accesible y dispuesto/a a ayudarte cuando lo necesitabas?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'profesorAportaRecursosNecesarios' => [
'tituloPregunta' => '¿Utilizó recursos didácticos útiles y aportó los materiales necesarios (presentaciones, ejemplos, casos prácticos, etc.)?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'valoracionProfesorCurso' => [
'tituloPregunta' => 'En general, ¿cómo valorarías el profesor/a del curso en una escala del 1 al 10?',
'respuestas' => $respuestasEscala10,
'totalRespuestas' => 0
],
'tutorOrientaBienPracticas' => [
'tituloPregunta' => '¿El profesor/a de prácticas te ofreció una buena orientación inicial sobre tu rol y tareas en la empresa?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'tutorRealizoSeguimientoBienPracticas' => [
'tituloPregunta' => '¿Realizó un seguimiento adecuado de tu evolución durante las prácticas?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'tutorAcompaniaBienPractica' => [
'tituloPregunta' => '¿Te sentiste acompañado/a por el profesor/a durante la etapa práctica?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'tutorResolutivoEnPracticas' => [
'tituloPregunta' => '¿Facilitó la resolución de posibles dudas o incidencias surgidas durante las prácticas?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'tutorProporcionaMaterialEnPracticas' => [
'tituloPregunta' => '¿Te proporcionó los materiales necesarios para el desarrollo de las prácticas?',
'respuestas' => $respuestasCuatroNiveles,
'totalRespuestas' => 0
],
'valoracionTutorPracticas' => [
'tituloPregunta' => 'En general, ¿cómo valorarías el tutor/a de prácticas en una escala del 1 al 10?',
'respuestas' => $respuestasEscala10,
'totalRespuestas' => 0
],
'opinionDuracionCursoEnGeneral' => [
'tituloPregunta' => '¿Qué te ha parecido la duración del curso formativo en general?',
'respuestas' => [
'Muy adecuada, se ajustó bien a los contenidos y ritmo' => 0,
'Adecuada, aunque podría haber sido un poco más extensa' => 0,
'Demasiado larga para el contenido impartido' => 0,
'Demasiado corta, no dio tiempo a profundizar lo suficiente' => 0
],
'totalRespuestas' => 0
],
'valoracionGeneralFormacion' => [
'tituloPregunta' => 'En general, ¿cómo valorarías la formación en su conjunto en una escala del 1 al 10?',
'respuestas' => $respuestasEscala10,
'totalRespuestas' => 0
],
'proyectosInmediatos' => [
'tituloPregunta' => 'A partir de tu participación en el curso, ¿cuáles son tus proyectos más inmediatos en relación con la formación?',
'respuestas' => [
'Buscar trabajo relacionado con mi nueva formación' => 0,
'Retomar y completar mis estudios' => 0,
'Realizar nuevos cursos de formación, profundizando en el sector del curso realizado' => 0,
'Realizar nuevos cursos de formación, en temática diferente al curso realizado' => 0,
'Otro' => 0
],
'totalRespuestas' => 0
],
];
}
// Esta función se utiliza en la vista detalle del curso, en la de test de evaluación.
public function estadisticaTestEvaluacion(Curso $curso): array
{
$alumnos = $this->em->getRepository(Alumno::class)
->createQueryBuilder('a')
->join('a.solicitud', 's')
->join('s.curso', 'c')
->andWhere('c = :curso')
->setParameter('curso', $curso)
->getQuery()
->getResult();
$result = $this->getEstructuraPreguntasBase();
// Mapa de claves a recorrer
$claves = array_keys($result);
/** @var Alumno $a */
foreach ($alumnos as $a) {
$test = $a->getTestEvaluacion();
if (!$test) {
continue;
}
$respuestasTest = $test->getRespuestas();
foreach ($claves as $clave) {
if (!isset($respuestasTest[$clave])) {
continue;
}
$this->acumularRespuestas($result, $respuestasTest[$clave], $clave);
}
}
// Calcular porcentajes
foreach ($claves as $clave) {
$total = $result[$clave]['totalRespuestas'] ?: 1;
foreach ($result[$clave]['respuestas'] as &$valor) {
$valor = number_format(($valor / $total) * 100, 2);
}
}
return $result;
}
// Función que se usa en la función "estadisticaTestEvaluacion".
private function acumularRespuestas(array &$result, array|string $respuesta, string $clave): void
{
// Asegurar que siempre trabajamos con un array
$respuestas = is_array($respuesta) ? $respuesta : [$respuesta];
foreach ($respuestas as $r) {
if (!isset($result[$clave]['respuestas'][$r])) {
continue; // ignorar respuestas inesperadas
}
$result[$clave]['respuestas'][$r]++;
$result[$clave]['totalRespuestas']++;
}
}
// Esta función permite obtener los campos de importes estimados y reales del financiero de un curso.
public function obtenerImportesEstimadosRealesFinanciero(Curso $curso): array
{
$plazas = $curso->getAforoTitulares() ?? 0;
$precioHoraFormador = $curso->getCosteHoraFormacion() ?? 0;
$totalHorasEnSegCurso = $curso->getHorasDuracion() ?? 0; // $curso->getHorasDuracion()/60/60
$totalHorasCursoFormador = $totalHorasEnSegCurso/60/60;
$totalDiasCursoAlumno = round($totalHorasEnSegCurso/60/60/5); // Redondeamos hacia arriba o abajo según dijeron.
$costeDiaAsistenciaAlumno = $curso->getCosteDiaAsistencia() ?? 0;
$personasFormadas = $this->em->getRepository(Alumno::class)->obtenerPersonasFormadas($curso);
$numAlumnosPersonaFormada = count($personasFormadas);
$totalDiasAlumnos = 0;
/** @var Alumno $persona */
foreach ($personasFormadas as $persona) {
$totalDiasAsisteAlumno = $totalDiasCursoAlumno;
foreach($persona->getAsistencias() as $asistencia) {
if ($asistencia->getTipo()->getId() === 2) {
$totalDiasAsisteAlumno -= $asistencia->getHorasEnSegundos() / 60 / 60 / 5;
}
}
$totalDiasAsisteAlumno = max(0, $totalDiasAsisteAlumno); // Si es negativa, se pone a 0.
$totalDiasAlumnos += $totalDiasAsisteAlumno;
}
return [
'importeEstimadoFormadores' => $precioHoraFormador * $plazas * $totalHorasCursoFormador,
'importeEstimadoAlumnos' => $plazas * $costeDiaAsistenciaAlumno * $totalDiasCursoAlumno,
'importeRealFormadores' => $precioHoraFormador * $numAlumnosPersonaFormada * $totalHorasCursoFormador,
'importeRealAlumnos' => $costeDiaAsistenciaAlumno * $totalDiasAlumnos
];
}
/**
* @throws MpdfException
* @throws SyntaxError
* @throws RuntimeError
* @throws LoaderError
*/
public function generarPdfInfoCurso(Curso $curso, string $dest = Destination::FILE): void
{
// Cargar plantilla
$fs = new Filesystem();
$nombreFichero = md5(uniqid()).'.docx';
$carpetaFichero = "tmp";
$nombreCompleto = "$carpetaFichero/$nombreFichero";
$datosPlantilla = [
'nombreCurso' => $curso->getNombre(),
'localidad' => $curso->getLocalidad()->getNombre(),
'ayudaMaxima' => $curso->getAyudaMaxima() ? "{$curso->getAyudaMaxima()}€" : 'No indicado',
'nivelAcceso' => $curso->getNivelFormativo()->getDescripcionNivelPlantilla(),
'descripcionCurso' => $curso->getDescripcionCurso(),
'horasFormacionEspecifica' => $curso->getHorasFormacionEspecifica()/60/60,
'horasFormacionTransversal' => $curso->getHorasFormacionTransversal()/60/60,
'horasFormacionComplementaria' => $curso->getHorasFormacionComplementaria()/60/60,
'horasTutoriaEspecifica' => $curso->getHorasTutoriaEspecifica()/60/60,
'horasTutoriaPractica' => $curso->getHorasTutoriaPractica()/60/60,
'horasPracticaProfesional' => $curso->getHorasPracticaProfesional()/60/60,
];
$cuerpo = $this->environment->render('plantillas-pdf/pdf-a4-info-curso.html.twig', $datosPlantilla);
$cabecera = "
<div>
<img style='width: 30mm' src='assets/img/logo-cabecera-pdfs.png' alt=''>
</div>
";
$pie = "
<div style='font-size: 7px; text-align: center;'>
<img src='assets/img/logo-pie-pdfs.png' alt=''>
</div>
<div style='font-size: 12px; border: 0; text-align: right'>
{PAGENO}/{nb}
</div>
";
$mpdf = new mPDF(['setAutoBottomMargin' => 'stretch']);
// $mpdf->showImageErrors = true;
$mpdf->SetHTMLHeader($cabecera);
$mpdf->SetHTMLFooter($pie);
$mpdf->AddPage('P','','','','',20,20,35,100,10,5);
$mpdf->WriteHTML($cuerpo);
if (!$fs->exists($carpetaFichero)) {
$fs->mkdir($carpetaFichero);
}
if ($dest === Destination::FILE) {
$mpdf->Output($nombreCompleto, Destination::FILE);
} else {
$mpdf->Output();
}
}
}