preguntasAsignadas()->exists()) { return [ 'success' => false, 'message' => 'El examen ya tiene preguntas' ]; } $reglas = ReglaAreaProceso::where('area_proceso_id', $examen->area_proceso_id) ->orderBy('orden') ->get(); if ($reglas->isEmpty()) { return [ 'success' => false, 'message' => 'No hay reglas configuradas' ]; } DB::beginTransaction(); try { $orden = 1; foreach ($reglas as $regla) { $preguntas = Pregunta::where('curso_id', $regla->curso_id) ->where('activo', 1) ->when($regla->nivel_dificultad, fn ($q) => $q->where('nivel_dificultad', $regla->nivel_dificultad) ) ->inRandomOrder() ->limit($regla->cantidad_preguntas) ->get(); if ($preguntas->count() < $regla->cantidad_preguntas) { throw new \Exception("Preguntas insuficientes para curso {$regla->curso_id}"); } foreach ($preguntas as $pregunta) { PreguntaAsignada::create([ 'examen_id' => $examen->id, 'pregunta_id' => $pregunta->id, 'orden' => $orden++, 'puntaje_base' => $regla->ponderacion, 'estado' => 'pendiente', ]); } } DB::commit(); return ['success' => true]; } catch (\Throwable $e) { DB::rollBack(); return ['success' => false, 'message' => $e->getMessage()]; } } public function obtenerPreguntasExamen(Examen $examen): array { // Traemos preguntas con curso $preguntas = $examen->preguntasAsignadas() ->with('pregunta.curso') ->get() ->sortBy('orden'); // Traemos datos del área-proceso directamente desde la DB $areaProceso = \DB::table('area_proceso') ->join('procesos', 'area_proceso.proceso_id', '=', 'procesos.id') ->join('areas', 'area_proceso.area_id', '=', 'areas.id') ->where('area_proceso.id', $examen->area_proceso_id) ->select( 'procesos.nombre as proceso_nombre', 'procesos.duracion as proceso_duracion', 'procesos.intentos_maximos as proceso_intentos_maximos', 'areas.nombre as area_nombre' ) ->first(); return $preguntas->map(fn($pa) => [ 'id' => $pa->id, 'orden' => $pa->orden, 'enunciado' => $pa->pregunta->enunciado, 'extra' => $pa->pregunta->enunciado_adicional, 'opciones' => $this->mezclarOpciones($pa->pregunta->opciones), 'imagenes' => $pa->pregunta->imagenes, 'estado' => $pa->estado, 'respuesta' => $pa->pregunta->respuesta_correcta, 'curso' => $pa->pregunta->curso->nombre ?? null, 'proceso' => $areaProceso->proceso_nombre ?? null, 'duracion' => $areaProceso->proceso_duracion ?? null, 'intentos_maximos'=> $areaProceso->proceso_intentos_maximos ?? null, 'area' => $areaProceso->area_nombre ?? null ])->values()->toArray(); } public function guardarRespuesta(PreguntaAsignada $pa, ?string $respuesta): array { if ($pa->estado === 'respondida') { return ['success' => false, 'message' => 'Ya respondida']; } // 🔹 Si está en blanco if (empty($respuesta)) { $pa->update([ 'respuesta_usuario' => null, 'es_correcta' => 2, // 2 = blanco 'puntaje_obtenido' => 0, 'estado' => 'respondida', 'respondida_at' => now() ]); return [ 'success' => true, 'correcta' => 2, 'puntaje' => 0 ]; } // 🔹 Si respondió algo $esCorrecta = $respuesta === $pa->pregunta->respuesta_correcta; $pa->update([ 'respuesta_usuario' => $respuesta, 'es_correcta' => $esCorrecta ? 1 : 0, 'puntaje_obtenido' => $esCorrecta ? $pa->puntaje_base : 0, 'estado' => 'respondida', 'respondida_at' => now() ]); return [ 'success' => true, 'correcta' => $esCorrecta ? 1 : 0, 'puntaje' => $pa->puntaje_obtenido ]; } /** * Finalizar examen */ public function finalizarExamen(Examen $examen): void { $examen->update([ 'estado' => 'finalizado', 'hora_fin' => now() ]); } private function mezclarOpciones(?array $opciones): array { if (!$opciones) return []; $keys = array_keys($opciones); shuffle($keys); return array_map(fn ($k) => [ 'key' => $k, 'texto' => $opciones[$k] ], $keys); } }