Modificación del de vista de dopcumentos del alumno

This commit is contained in:
2026-05-06 18:33:18 -06:00
parent 91d0813a9c
commit 964ed4bbd2
27 changed files with 1921 additions and 339 deletions
+3 -3
View File
@@ -51,9 +51,7 @@ class AlumnoController extends Controller
})
->get();
}
if ($user->hasRole('Control escolar')) {
}else{
$plantelesUsuario = $user->plantelUsuarios->pluck('id');
$alumnos = Alumno::with(['alumnos.plantelUsuarios','statusAlumno'])
@@ -65,6 +63,8 @@ class AlumnoController extends Controller
}
return view('alumno.index', compact('alumnos'));
}
+31 -12
View File
@@ -9,6 +9,7 @@ use App\Models\Tarea;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class EntregaController extends Controller
{
@@ -25,12 +26,23 @@ class EntregaController extends Controller
]);
}
$cicloActual = Campan::where('status', 1)->first();
// Ciclo más alto del alumno = semestre en curso
$maxCiclo = DB::table('alumno_grupo')
->join('grupos', 'grupos.id', '=', 'alumno_grupo.grupo_id')
->where('alumno_grupo.alumno_id', $alumno->id)
->max('grupos.ciclo');
// Solo grupos del ciclo activo
$gruposActuales = $alumno->grupos()
->when($cicloActual, fn($q) => $q->where('ciclo', $cicloActual->id))
->pluck('grupos.id');
// Campan para mostrar el nombre en la vista (busca por id = ciclo, o cae al activo)
$cicloActual = $maxCiclo ? Campan::find($maxCiclo) ?? Campan::where('status', 1)->first() : null;
// Grupos del alumno en ese ciclo
$gruposActuales = $maxCiclo
? DB::table('alumno_grupo')
->join('grupos', 'grupos.id', '=', 'alumno_grupo.grupo_id')
->where('alumno_grupo.alumno_id', $alumno->id)
->where('grupos.ciclo', $maxCiclo)
->pluck('alumno_grupo.grupo_id')
: collect();
$todasTareas = Tarea::whereIn('grupo_id', $gruposActuales)
->with(['material', 'grupo'])
@@ -59,15 +71,21 @@ class EntregaController extends Controller
public function store(Request $request, Tarea $tarea)
{
$request->validate([
'archivo' => 'nullable|file|max:20480',
'archivo' => 'nullable|file|max:2048',
'texto' => 'nullable|string|max:5000',
]);
if (!$request->hasFile('archivo') && !filled($request->texto)) {
return back()->withErrors(['entrega' => 'Debes escribir un texto o adjuntar un archivo.']);
return redirect()->route('mis.tareas')
->withErrors(['entrega' => 'Debes escribir un texto o adjuntar un archivo.']);
}
$alumno = Alumno::whereHas('alumnos', fn($q) => $q->where('id', Auth::id()))->firstOrFail();
$alumno = Alumno::whereHas('alumnos', fn($q) => $q->where('id', Auth::id()))->first();
if (!$alumno) {
return redirect()->route('mis.tareas')
->with('error', 'No se encontró un perfil de alumno para tu cuenta.');
}
$entrega = Entrega::where('tarea_id', $tarea->id)
->where('alumno_id', $alumno->id)
@@ -82,12 +100,12 @@ class EntregaController extends Controller
}
if ($entrega->status !== 'sin_subir') {
return back()->with('error', 'Ya entregaste esta tarea y no puedes volver a subirla.');
return redirect()->route('mis.tareas')
->with('error', 'Ya entregaste esta tarea y no puedes volver a subirla.');
}
$path = null;
$path = $entrega->archivo;
if ($request->hasFile('archivo')) {
// Almacena en disco local (privado) igual que el FileController espera
$path = $request->file('archivo')->store("entregas/{$tarea->id}");
}
@@ -97,6 +115,7 @@ class EntregaController extends Controller
'status' => 'pendiente',
]);
return back()->with('info', 'Tarea enviada correctamente. Queda pendiente de calificación.');
return redirect()->route('mis.tareas')
->with('info', 'Tarea enviada correctamente. Queda pendiente de calificación.');
}
}
@@ -0,0 +1,222 @@
<?php
namespace App\Http\Controllers;
use App\Models\Docente;
use App\Models\Evaluacion;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Http;
class EvaluacionReporteController extends Controller
{
public function show(Evaluacion $evaluacion)
{
$datos = $this->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;
}
}
@@ -98,12 +98,35 @@ class EvaluacionResponderController extends Controller
if ($tipoSlug === 'alumno' && $preguntas->where('es_docente', true)->count() > 0) {
$alumno = Alumno::whereHas('alumnos', fn($q) => $q->where('id', Auth::id()))->first();
if ($alumno) {
$gruposIds = $alumno->grupos()->pluck('grupos.id');
$docentesParaEvaluar = Docente::whereHas('grupos', fn($q) => $q->whereIn('grupos.id', $gruposIds))
->with('docentes')
->get()
->unique('id')
->values();
// Ciclo más alto del alumno = semestre actual
$cicloActual = DB::table('alumno_grupo')
->join('grupos', 'grupos.id', '=', 'alumno_grupo.grupo_id')
->where('alumno_grupo.alumno_id', $alumno->id)
->max('grupos.ciclo');
if ($cicloActual) {
// Grupos del alumno en ese ciclo
$gruposIds = DB::table('alumno_grupo')
->join('grupos', 'grupos.id', '=', 'alumno_grupo.grupo_id')
->where('alumno_grupo.alumno_id', $alumno->id)
->where('grupos.ciclo', $cicloActual)
->pluck('alumno_grupo.grupo_id')
->toArray();
// Docentes asignados a esos grupos cuya materia tenga el mismo ciclo que el grupo
$docenteIds = DB::table('docente_grupo')
->join('materials', 'materials.id', '=', 'docente_grupo.materia_id')
->whereIn('docente_grupo.grupo_id', $gruposIds)
->where('materials.ciclo', $cicloActual)
->distinct()
->pluck('docente_grupo.docente_id')
->toArray();
$docentesParaEvaluar = Docente::whereIn('id', $docenteIds)
->with('docentes')
->get()
->values();
}
}
}
+1 -1
View File
@@ -27,7 +27,7 @@ class GrupoController extends Controller
{
$user = Auth::user();
if ($user->hasRole('Admin')) {
if ($user->hasRole('Administrador')) {
$grupos = Grupo::all();
}
@@ -0,0 +1,10 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ProspectoController extends Controller
{
//
}