buildDatos($evaluacion); $totalSubmissions = $evaluacion->submissions()->count(); return view('evaluacion.reporte', compact('evaluacion', 'datos', 'totalSubmissions')); } public function pdf(Evaluacion $evaluacion) { $datos = $this->buildDatos($evaluacion); $totalSubmissions = $evaluacion->submissions()->count(); // Fetch chart images from QuickChart and embed as base64 foreach ($datos as &$item) { if ($item['tipo'] === 'texto_libre') continue; if ($item['es_docente']) { foreach ($item['por_docente'] as &$pd) { if (isset($pd['chart_config'])) { $pd['chart_img'] = $this->fetchChartBase64($pd['chart_config']); } } } else { if (isset($item['chart_config'])) { $item['chart_img'] = $this->fetchChartBase64($item['chart_config']); } } } $pdf = Pdf::loadView('evaluacion.reporte_pdf', compact('evaluacion', 'datos', 'totalSubmissions')) ->setPaper('a4', 'portrait'); return $pdf->download('reporte-' . $evaluacion->id . '.pdf'); } // ─── Data builder ─────────────────────────────────────────────────────────── private function buildDatos(Evaluacion $evaluacion): array { $preguntas = $evaluacion->preguntas() ->with(['tipo', 'opciones', 'respuestas.opcion']) ->get(); $datos = []; foreach ($preguntas as $pregunta) { $tipoSlug = $pregunta->tipo->slug; $respuestas = $pregunta->respuestas; if ($pregunta->es_docente) { $docenteIds = $respuestas->pluck('docente_id')->unique()->filter(); $porDocente = []; foreach ($docenteIds as $docenteId) { $rsp = $respuestas->where('docente_id', $docenteId); $docente = Docente::with('docentes')->find($docenteId); $user = $docente?->docentes->first(); $nombre = $user ? trim("{$user->apellidoPaterno} {$user->apellidoMaterno} {$user->name}") : "Docente #{$docenteId}"; $chart = $this->procesarRespuestas($rsp, $tipoSlug, $pregunta); $porDocente[] = [ 'docente_nombre' => $nombre, 'chart_config' => $chart['config'] ?? null, 'textos' => $chart['textos'] ?? [], 'promedio' => $chart['promedio'] ?? null, 'total' => $rsp->count(), ]; } $datos[] = [ 'pregunta' => $pregunta, 'tipo' => $tipoSlug, 'es_docente' => true, 'por_docente' => $porDocente, ]; } else { $chart = $this->procesarRespuestas($respuestas, $tipoSlug, $pregunta); $datos[] = [ 'pregunta' => $pregunta, 'tipo' => $tipoSlug, 'es_docente' => false, 'chart_config' => $chart['config'] ?? null, 'textos' => $chart['textos'] ?? [], 'promedio' => $chart['promedio'] ?? null, 'total' => $respuestas->count(), ]; } } return $datos; } private function procesarRespuestas($respuestas, string $tipoSlug, $pregunta): array { switch ($tipoSlug) { case 'si_no': $si = $respuestas->where('valor', 'si')->count(); $no = $respuestas->where('valor', 'no')->count(); return ['config' => [ 'type' => 'doughnut', 'data' => [ 'labels' => ['Sí', 'No'], 'datasets' => [[ 'data' => [$si, $no], 'backgroundColor' => ['#1cc88a', '#e74a3b'], 'borderWidth' => 2, ]], ], 'options' => [ 'plugins' => [ 'legend' => ['position' => 'bottom'], 'datalabels' => ['display' => false], ], ], ]]; case 'opcion_multiple': $labels = []; $counts = []; $colors = ['#4e73df','#1cc88a','#36b9cc','#f6c23e','#e74a3b','#858796','#5a5c69']; foreach ($pregunta->opciones as $idx => $opcion) { $labels[] = $opcion->texto; $counts[] = $respuestas->where('opcion_id', $opcion->id)->count(); } return ['config' => [ 'type' => 'bar', 'data' => [ 'labels' => $labels, 'datasets' => [[ 'label' => 'Respuestas', 'data' => $counts, 'backgroundColor' => array_slice(array_merge($colors, $colors), 0, count($labels)), 'borderRadius' => 4, ]], ], 'options' => [ 'plugins' => ['legend' => ['display' => false]], 'scales' => ['y' => ['beginAtZero' => true, 'ticks' => ['stepSize' => 1]]], ], ]]; case 'escala_5': case 'escala_10': $max = $tipoSlug === 'escala_5' ? 5 : 10; $labels = range(1, $max); $counts = []; foreach ($labels as $val) { $counts[] = $respuestas->filter(fn($r) => (int)$r->valor === $val)->count(); } $total = $respuestas->count(); $suma = $respuestas->sum(fn($r) => (int)$r->valor); $prom = $total > 0 ? round($suma / $total, 2) : null; return [ 'config' => [ 'type' => 'bar', 'data' => [ 'labels' => $labels, 'datasets' => [[ 'label' => 'Respuestas', 'data' => $counts, 'backgroundColor' => '#36b9cc', 'borderRadius' => 4, ]], ], 'options' => [ 'plugins' => ['legend' => ['display' => false]], 'scales' => ['y' => ['beginAtZero' => true, 'ticks' => ['stepSize' => 1]]], ], ], 'promedio' => $prom, ]; case 'texto_libre': return ['textos' => $respuestas->pluck('valor')->filter()->values()->toArray()]; default: return []; } } // ─── QuickChart helper ─────────────────────────────────────────────────────── private function fetchChartBase64(array $config): ?string { try { $response = Http::timeout(15)->post('https://quickchart.io/chart', [ 'chart' => $config, 'width' => 500, 'height' => 280, 'backgroundColor' => 'white', 'format' => 'png', ]); if ($response->successful()) { return 'data:image/png;base64,' . base64_encode($response->body()); } } catch (\Throwable) { // QuickChart no disponible; la imagen simplemente no aparece } return null; } }