|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
|
use App\Models\Postulante;
|
|
|
|
|
use App\Models\Proceso;
|
|
|
|
|
use App\Models\Area;
|
|
|
|
|
use App\Models\Curso;
|
|
|
|
|
use App\Models\Pregunta;
|
|
|
|
|
use App\Models\Alternativa;
|
|
|
|
|
use App\Models\Examen;
|
|
|
|
|
use App\Models\PreguntaAsignada;
|
|
|
|
|
use App\Models\AlternativaAsignada;
|
|
|
|
|
use App\Models\DetalleResultado;
|
|
|
|
|
use App\Models\RespuestaPostulante;
|
|
|
|
|
use App\Models\ResultadoExamen;
|
|
|
|
|
use App\Models\Recomendaciones;
|
|
|
|
|
use App\Models\Pago;
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
use Illuminate\Support\Facades\Auth;
|
|
|
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
|
use App\Services\ExamenService;
|
|
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
|
|
|
|
class ExamenController extends Controller
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
protected $examenService;
|
|
|
|
|
|
|
|
|
|
public function __construct(ExamenService $examenService)
|
|
|
|
|
{
|
|
|
|
|
$this->examenService = $examenService;
|
|
|
|
|
}
|
|
|
|
|
public function procesoexamen(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$postulante = $request->user();
|
|
|
|
|
|
|
|
|
|
$procesos = Proceso::where('activo', 1)
|
|
|
|
|
->whereNotExists(function ($q) use ($postulante) {
|
|
|
|
|
$q->select(\DB::raw(1))
|
|
|
|
|
->from('examenes')
|
|
|
|
|
->join('area_proceso', 'area_proceso.id', '=', 'examenes.area_proceso_id')
|
|
|
|
|
->whereColumn('area_proceso.proceso_id', 'procesos.id')
|
|
|
|
|
->where('examenes.postulante_id', $postulante->id);
|
|
|
|
|
})
|
|
|
|
|
->select('id', 'nombre', 'requiere_pago')
|
|
|
|
|
->get();
|
|
|
|
|
|
|
|
|
|
return response()->json($procesos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function areas(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$procesoId = $request->query('proceso_id');
|
|
|
|
|
|
|
|
|
|
$areas = Area::select('areas.id', 'areas.nombre')
|
|
|
|
|
->whereHas('procesos', function ($query) use ($procesoId) {
|
|
|
|
|
$query->where('procesos.id', $procesoId)
|
|
|
|
|
->where('procesos.activo', 1);
|
|
|
|
|
})
|
|
|
|
|
->with(['procesos' => function ($q) use ($procesoId) {
|
|
|
|
|
$q->where('procesos.id', $procesoId)
|
|
|
|
|
->select('procesos.id')
|
|
|
|
|
->withPivot('id', 'proceso_id', 'area_id');
|
|
|
|
|
}])
|
|
|
|
|
->get()
|
|
|
|
|
->map(function ($area) {
|
|
|
|
|
$pivot = $area->procesos->first()->pivot;
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'area_id' => $area->id,
|
|
|
|
|
'nombre' => $area->nombre,
|
|
|
|
|
'area_proceso_id' => $pivot->id,
|
|
|
|
|
];
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return response()->json($areas);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function crearExamen(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$postulante = $request->user();
|
|
|
|
|
|
|
|
|
|
$request->validate([
|
|
|
|
|
'area_proceso_id' => 'required|exists:area_proceso,id',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// 🔥 Obtener TODO desde el pivot
|
|
|
|
|
$areaProceso = \DB::table('area_proceso')
|
|
|
|
|
->join('procesos', 'procesos.id', '=', 'area_proceso.proceso_id')
|
|
|
|
|
->join('areas', 'areas.id', '=', 'area_proceso.area_id')
|
|
|
|
|
->where('area_proceso.id', $request->area_proceso_id)
|
|
|
|
|
->select(
|
|
|
|
|
'area_proceso.id',
|
|
|
|
|
'area_proceso.area_id',
|
|
|
|
|
'area_proceso.proceso_id',
|
|
|
|
|
'procesos.requiere_pago'
|
|
|
|
|
)
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
if (!$areaProceso) {
|
|
|
|
|
return response()->json(['message' => 'Relación área-proceso inválida'], 400);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$yaDioProceso = Examen::join('area_proceso', 'area_proceso.id', '=', 'examenes.area_proceso_id')
|
|
|
|
|
->where('examenes.postulante_id', $postulante->id)
|
|
|
|
|
->where('area_proceso.proceso_id', $areaProceso->proceso_id)
|
|
|
|
|
->exists();
|
|
|
|
|
|
|
|
|
|
if ($yaDioProceso) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'message' => 'Ya rendiste un examen para este proceso'
|
|
|
|
|
], 400);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$pagado = 0;
|
|
|
|
|
$pagoId = null;
|
|
|
|
|
|
|
|
|
|
// 💰 Validación de pago
|
|
|
|
|
if ($areaProceso->requiere_pago) {
|
|
|
|
|
$request->validate([
|
|
|
|
|
'tipo_pago' => 'required|in:pyto_peru,banco_nacion,caja',
|
|
|
|
|
'codigo_pago' => 'required',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$response = $this->validarPago(
|
|
|
|
|
$request->tipo_pago,
|
|
|
|
|
$request->codigo_pago,
|
|
|
|
|
$postulante->dni
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!$response['estado']) {
|
|
|
|
|
return response()->json(['message' => 'Pago inválido'], 400);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$pago = Pago::firstOrCreate(
|
|
|
|
|
[
|
|
|
|
|
'codigo_pago' => $request->codigo_pago,
|
|
|
|
|
'tipo_pago' => $request->tipo_pago,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'postulante_id' => $postulante->id,
|
|
|
|
|
'monto' => $response['monto'],
|
|
|
|
|
'fecha_pago' => $response['fecha_pago'],
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ($pago->utilizado) {
|
|
|
|
|
return response()->json(['message' => 'Pago ya utilizado'], 400);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$pago->update(['utilizado' => true]);
|
|
|
|
|
|
|
|
|
|
$pagado = 1;
|
|
|
|
|
$pagoId = $pago->id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 🧠 Crear / actualizar examen
|
|
|
|
|
$examen = Examen::updateOrCreate(
|
|
|
|
|
['postulante_id' => $postulante->id,
|
|
|
|
|
'area_proceso_id' => $areaProceso->id, // 🔥 pivot
|
|
|
|
|
'pagado' => $pagado,
|
|
|
|
|
'tipo_pago' => $request->tipo_pago ?? null,
|
|
|
|
|
'pago_id' => $pagoId,
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => true,
|
|
|
|
|
'examen_id' => $examen->id,
|
|
|
|
|
'mensaje' => 'Examen creado correctamente',
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function validarPago($tipoPago, $codigoPago, $dni)
|
|
|
|
|
{
|
|
|
|
|
return match ($tipoPago) {
|
|
|
|
|
'pyto_peru' => $this->validarPagoPytoPeru($codigoPago, $dni),
|
|
|
|
|
'banco_nacion' => $this->validarPagoBancoNacion($codigoPago, $dni),
|
|
|
|
|
'caja' => $this->validarPagoCaja($codigoPago, $dni),
|
|
|
|
|
default => ['estado' => false],
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function validarPagoBancoNacion($secuencia, $dni)
|
|
|
|
|
{
|
|
|
|
|
$url = 'https://inscripciones.admision.unap.edu.pe/api/get-pagos-banco-secuencia';
|
|
|
|
|
|
|
|
|
|
$response = Http::post($url, [
|
|
|
|
|
'secuencia' => $secuencia,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if (!$response->successful()) {
|
|
|
|
|
\Log::error('Error en la solicitud a la API', ['status' => $response->status(), 'body' => $response->body()]);
|
|
|
|
|
return ['estado' => false, 'message' => 'Error en la solicitud a la API'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = $response->json();
|
|
|
|
|
|
|
|
|
|
if (isset($data['estado']) && $data['estado'] === true) {
|
|
|
|
|
foreach ($data['datos'] as $pago) {
|
|
|
|
|
if (isset($pago['dni']) && trim($pago['dni']) == trim($dni)) {
|
|
|
|
|
return [
|
|
|
|
|
'estado' => true,
|
|
|
|
|
'monto' => $pago['imp_pag'],
|
|
|
|
|
'fecha_pago' => $pago['fch_pag'],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ['estado' => false, 'message' => 'Datos no válidos o no encontrados'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function validarPagoPytoPeru($authorizationCode, $dni)
|
|
|
|
|
{
|
|
|
|
|
$url = "https://service2.unap.edu.pe/PAYMENTS_MNG/v1/{$dni}/8/";
|
|
|
|
|
$response = Http::get($url);
|
|
|
|
|
|
|
|
|
|
if ($response->successful()) {
|
|
|
|
|
$data = $response->json();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (isset($data['data'][0]['autorizationCode']) && $data['data'][0]['autorizationCode'] === $authorizationCode) {
|
|
|
|
|
return [
|
|
|
|
|
'estado' => true,
|
|
|
|
|
'monto' => $data['data'][0]['total'],
|
|
|
|
|
'fecha_pago' => $data['data'][0]['confirmedDate'],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ['estado' => false, 'message' => 'El código de autorización no coincide.'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function validarPagoCaja($codigoPago, $dni)
|
|
|
|
|
{
|
|
|
|
|
$url = "https://inscripciones.admision.unap.edu.pe/api/get-pago-caja/{$dni}/{$codigoPago}";
|
|
|
|
|
|
|
|
|
|
$response = Http::get($url);
|
|
|
|
|
|
|
|
|
|
if (!$response->successful()) {
|
|
|
|
|
\Log::error('Error API Caja', [
|
|
|
|
|
'status' => $response->status(),
|
|
|
|
|
'body' => $response->body()
|
|
|
|
|
]);
|
|
|
|
|
return ['estado' => false, 'message' => 'Error en la API de Caja'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = $response->json();
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
isset($data['paymentTitle'], $data['paymentAmount'], $data['paymentDatetime']) &&
|
|
|
|
|
trim($data['paymentTitle']) === trim($codigoPago)
|
|
|
|
|
) {
|
|
|
|
|
return [
|
|
|
|
|
'estado' => true,
|
|
|
|
|
'monto' => (float) $data['paymentAmount'],
|
|
|
|
|
'fecha_pago' => $data['paymentDatetime'],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ['estado' => false, 'message' => 'Pago no válido o no encontrado'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function miExamenActual(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$postulante = $request->user();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$examen = Examen::join('area_proceso', 'area_proceso.id', '=', 'examenes.area_proceso_id')
|
|
|
|
|
->join('areas', 'areas.id', '=', 'area_proceso.area_id')
|
|
|
|
|
->join('procesos', 'procesos.id', '=', 'area_proceso.proceso_id')
|
|
|
|
|
->where('examenes.postulante_id', $postulante->id)
|
|
|
|
|
->select(
|
|
|
|
|
'examenes.id',
|
|
|
|
|
'examenes.pagado',
|
|
|
|
|
'examenes.tipo_pago',
|
|
|
|
|
'examenes.intentos',
|
|
|
|
|
'areas.id as area_id',
|
|
|
|
|
'areas.nombre as area_nombre',
|
|
|
|
|
'procesos.id as proceso_id',
|
|
|
|
|
'procesos.nombre as proceso_nombre',
|
|
|
|
|
'procesos.intentos_maximos as proceso_intentos_maximos'
|
|
|
|
|
)
|
|
|
|
|
->latest('examenes.created_at')
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
if (!$examen) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => true,
|
|
|
|
|
'mensaje' => 'No tienes exámenes asignados actualmente',
|
|
|
|
|
'examen' => null
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => true,
|
|
|
|
|
'examen' => [
|
|
|
|
|
'id' => $examen->id,
|
|
|
|
|
'intentos' => $examen->intentos,
|
|
|
|
|
'intentos_maximos' => $examen->proceso_intentos_maximos,
|
|
|
|
|
'proceso' => [
|
|
|
|
|
'id' => $examen->proceso_id,
|
|
|
|
|
'nombre' => $examen->proceso_nombre,
|
|
|
|
|
],
|
|
|
|
|
'area' => [
|
|
|
|
|
'id' => $examen->area_id,
|
|
|
|
|
'nombre' => $examen->area_nombre,
|
|
|
|
|
],
|
|
|
|
|
'pagado' => $examen->pagado,
|
|
|
|
|
'tipo_pago' => $examen->tipo_pago ?? null,
|
|
|
|
|
]
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function generarPreguntas($examenId)
|
|
|
|
|
{
|
|
|
|
|
$examen = Examen::findOrFail($examenId);
|
|
|
|
|
|
|
|
|
|
$postulante = request()->user();
|
|
|
|
|
if ($examen->postulante_id !== $postulante->id) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => false,
|
|
|
|
|
'message' => 'No autorizado'
|
|
|
|
|
], 403);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($examen->preguntasAsignadas()->exists()) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => true,
|
|
|
|
|
'message' => 'El examen ya tiene preguntas generadas',
|
|
|
|
|
'ya_tiene_preguntas' => true,
|
|
|
|
|
'total_preguntas' => $examen->preguntasAsignadas()->count()
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$resultado = $this->examenService->generarPreguntasExamen($examen);
|
|
|
|
|
|
|
|
|
|
if (!$resultado['success']) {
|
|
|
|
|
return response()->json($resultado, 400);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => true,
|
|
|
|
|
'message' => 'Preguntas generadas exitosamente',
|
|
|
|
|
'ya_tiene_preguntas' => false,
|
|
|
|
|
'total_preguntas' => $examen->preguntasAsignadas()->count()
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function iniciarExamen(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$request->validate([
|
|
|
|
|
'examen_id' => 'required|exists:examenes,id'
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$examen = Examen::findOrFail($request->examen_id);
|
|
|
|
|
|
|
|
|
|
$postulante = $request->user();
|
|
|
|
|
if ($examen->postulante_id !== $postulante->id) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => false,
|
|
|
|
|
'message' => 'No autorizado'
|
|
|
|
|
], 403);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$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.id as proceso_id',
|
|
|
|
|
'procesos.nombre as proceso_nombre',
|
|
|
|
|
'procesos.duracion as proceso_duracion',
|
|
|
|
|
'procesos.intentos_maximos as proceso_intentos_maximos',
|
|
|
|
|
'areas.nombre as area_nombre'
|
|
|
|
|
)
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
if (!$examen->preguntasAsignadas()->exists()) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => false,
|
|
|
|
|
'message' => 'El examen no tiene preguntas generadas'
|
|
|
|
|
], 400);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($areaProceso && $examen->intentos >= $areaProceso->proceso_intentos_maximos) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => false,
|
|
|
|
|
'message' => 'Has alcanzado el número máximo de intentos para este proceso'
|
|
|
|
|
], 403);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$examen->increment('intentos');
|
|
|
|
|
|
|
|
|
|
$examen->update([
|
|
|
|
|
'hora_inicio' => now(),
|
|
|
|
|
'estado' => 'en_progreso'
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$preguntas = $this->examenService->obtenerPreguntasExamen($examen);
|
|
|
|
|
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => true,
|
|
|
|
|
'examen' => [
|
|
|
|
|
'id' => $examen->id,
|
|
|
|
|
'estado' => $examen->estado,
|
|
|
|
|
'hora_inicio' => $examen->hora_inicio,
|
|
|
|
|
'intentos' => $examen->intentos,
|
|
|
|
|
'intentos_maximos' => $areaProceso->proceso_intentos_maximos ?? null,
|
|
|
|
|
'proceso' => $areaProceso->proceso_nombre ?? null,
|
|
|
|
|
'duracion' => $areaProceso->proceso_duracion ?? null,
|
|
|
|
|
'area' => $areaProceso->area_nombre ?? null
|
|
|
|
|
],
|
|
|
|
|
'preguntas' => $preguntas
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function responderPregunta($preguntaAsignadaId, Request $request)
|
|
|
|
|
{
|
|
|
|
|
$request->validate([
|
|
|
|
|
'respuesta' => 'nullable|string'
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$preguntaAsignada = PreguntaAsignada::with(['examen', 'pregunta'])
|
|
|
|
|
->findOrFail($preguntaAsignadaId);
|
|
|
|
|
|
|
|
|
|
$postulante = $request->user();
|
|
|
|
|
if ($preguntaAsignada->examen->postulante_id !== $postulante->id) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => false,
|
|
|
|
|
'message' => 'No autorizado'
|
|
|
|
|
], 403);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$resultado = $this->examenService->guardarRespuesta(
|
|
|
|
|
$preguntaAsignada,
|
|
|
|
|
$request->respuesta
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return response()->json($resultado);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function finalizarExamen($examenId)
|
|
|
|
|
{
|
|
|
|
|
$examen = Examen::findOrFail($examenId);
|
|
|
|
|
|
|
|
|
|
$postulante = request()->user();
|
|
|
|
|
|
|
|
|
|
// Validar que el examen le pertenezca al postulante autenticado
|
|
|
|
|
if ((int) $examen->postulante_id !== (int) $postulante->id) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => false,
|
|
|
|
|
'message' => 'No autorizado'
|
|
|
|
|
], 403);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// (Opcional) Evitar finalizar 2 veces
|
|
|
|
|
if ($examen->estado === 'finalizado') {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => false,
|
|
|
|
|
'message' => 'El examen ya está finalizado'
|
|
|
|
|
], 409);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Finalizar examen
|
|
|
|
|
$examen->update([
|
|
|
|
|
'estado' => 'finalizado',
|
|
|
|
|
'hora_fin' => now(),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => true,
|
|
|
|
|
'message' => 'Examen finalizado exitosamente'
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function calificarExamen($examenId, Request $request)
|
|
|
|
|
{
|
|
|
|
|
$postulante = $request->user();
|
|
|
|
|
if (!$postulante) {
|
|
|
|
|
return response()->json(['success' => false, 'mensaje' => 'No autenticado.'], 401);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DB::transaction(function () use ($examenId, $postulante) {
|
|
|
|
|
|
|
|
|
|
// 1) Validar examen del postulante
|
|
|
|
|
$examen = DB::table('examenes')
|
|
|
|
|
->where('id', $examenId)
|
|
|
|
|
->where('postulante_id', $postulante->id)
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
if (!$examen) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => false,
|
|
|
|
|
'mensaje' => 'No se encontró un examen para este postulante.'
|
|
|
|
|
], 404);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2) Si ya está calificado, devolver existente (y opcionalmente recalcular orden)
|
|
|
|
|
$existente = DB::table('resultados_examenes')
|
|
|
|
|
->where('examen_id', $examen->id)
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
if ($existente) {
|
|
|
|
|
// Obtener proceso_id para poder recalcular si deseas (opcional)
|
|
|
|
|
$procesoId = DB::table('area_proceso')
|
|
|
|
|
->where('id', $examen->area_proceso_id)
|
|
|
|
|
->value('proceso_id');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ($procesoId) $this->recalcularOrdenMerito($procesoId);
|
|
|
|
|
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => true,
|
|
|
|
|
'mensaje' => 'Examen ya calificado.',
|
|
|
|
|
'total_puntos' => (float)$existente->total_puntos,
|
|
|
|
|
'total_correctas' => (int)$existente->total_correctas,
|
|
|
|
|
'total_incorrectas' => (int)$existente->total_incorrectas,
|
|
|
|
|
'total_nulas' => (int)$existente->total_nulas,
|
|
|
|
|
'porcentaje_correctas' => (float)$existente->porcentaje_correctas,
|
|
|
|
|
'calificacion_sobre_20' => (float)$existente->calificacion_sobre_20,
|
|
|
|
|
'orden_merito' => $existente->orden_merito,
|
|
|
|
|
'correctas_por_curso' => json_decode($existente->correctas_por_curso, true),
|
|
|
|
|
'incorrectas_por_curso' => json_decode($existente->incorrectas_por_curso ?? '[]', true),
|
|
|
|
|
'preguntas_totales_por_curso' => json_decode($existente->preguntas_totales_por_curso ?? '[]', true),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3) Obtener configuración de calificación desde proceso -> calificaciones
|
|
|
|
|
$cfg = DB::table('area_proceso as ap')
|
|
|
|
|
->join('procesos as pr', 'pr.id', '=', 'ap.proceso_id')
|
|
|
|
|
->join('calificaciones as ca', 'ca.id', '=', 'pr.calificacion_id')
|
|
|
|
|
->where('ap.id', $examen->area_proceso_id)
|
|
|
|
|
->select(
|
|
|
|
|
'pr.id as proceso_id',
|
|
|
|
|
'ca.puntos_correcta',
|
|
|
|
|
'ca.puntos_incorrecta',
|
|
|
|
|
'ca.puntos_nula',
|
|
|
|
|
'ca.puntaje_maximo'
|
|
|
|
|
)
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
if (!$cfg) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => false,
|
|
|
|
|
'mensaje' => 'No se ha definido un tipo de calificación para este proceso.'
|
|
|
|
|
], 400);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$puntosCorrecta = (float) $cfg->puntos_correcta;
|
|
|
|
|
$puntosIncorrecta = (float) $cfg->puntos_incorrecta;
|
|
|
|
|
$puntosNula = (float) $cfg->puntos_nula;
|
|
|
|
|
$puntajeMaximo = (float) $cfg->puntaje_maximo;
|
|
|
|
|
|
|
|
|
|
// 4) Traer preguntas asignadas con su pregunta y curso
|
|
|
|
|
$items = DB::table('preguntas_asignadas as pa')
|
|
|
|
|
->join('preguntas as p', 'p.id', '=', 'pa.pregunta_id')
|
|
|
|
|
->join('cursos as c', 'c.id', '=', 'p.curso_id')
|
|
|
|
|
->where('pa.examen_id', $examen->id)
|
|
|
|
|
->select(
|
|
|
|
|
'pa.id as pa_id',
|
|
|
|
|
'pa.estado as pa_estado',
|
|
|
|
|
'pa.respuesta_usuario',
|
|
|
|
|
'p.respuesta_correcta',
|
|
|
|
|
'c.nombre as curso_nombre'
|
|
|
|
|
)
|
|
|
|
|
->get();
|
|
|
|
|
|
|
|
|
|
if ($items->isEmpty()) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => false,
|
|
|
|
|
'mensaje' => 'El examen no tiene preguntas asignadas.'
|
|
|
|
|
], 422);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5) Calificar y actualizar cada pregunta_asignada
|
|
|
|
|
$totalPuntos = 0.0;
|
|
|
|
|
$totalCorrectas = 0;
|
|
|
|
|
$totalIncorrectas = 0;
|
|
|
|
|
$totalNulas = 0;
|
|
|
|
|
|
|
|
|
|
$correctasPorCurso = [];
|
|
|
|
|
$incorrectasPorCurso = [];
|
|
|
|
|
$preguntasTotalesPorCurso = [];
|
|
|
|
|
|
|
|
|
|
foreach ($items as $row) {
|
|
|
|
|
$curso = $row->curso_nombre;
|
|
|
|
|
|
|
|
|
|
$preguntasTotalesPorCurso[$curso] = ($preguntasTotalesPorCurso[$curso] ?? 0) + 1;
|
|
|
|
|
$correctasPorCurso[$curso] = $correctasPorCurso[$curso] ?? 0;
|
|
|
|
|
$incorrectasPorCurso[$curso] = $incorrectasPorCurso[$curso] ?? 0;
|
|
|
|
|
|
|
|
|
|
$nuevoEsCorrecta = 2; // 2 = blanco
|
|
|
|
|
$nuevoPuntaje = $puntosNula; // nula/blanco
|
|
|
|
|
|
|
|
|
|
if ($row->pa_estado === 'anulada') {
|
|
|
|
|
// anulada => nula
|
|
|
|
|
$nuevoEsCorrecta = 2;
|
|
|
|
|
$nuevoPuntaje = $puntosNula;
|
|
|
|
|
$totalNulas++;
|
|
|
|
|
} else {
|
|
|
|
|
$ru = trim((string) $row->respuesta_usuario);
|
|
|
|
|
$rc = trim((string) $row->respuesta_correcta);
|
|
|
|
|
|
|
|
|
|
if ($ru === '') {
|
|
|
|
|
// blanco
|
|
|
|
|
$nuevoEsCorrecta = 2;
|
|
|
|
|
$nuevoPuntaje = $puntosNula;
|
|
|
|
|
$totalNulas++;
|
|
|
|
|
} else {
|
|
|
|
|
$ruN = mb_strtoupper($ru);
|
|
|
|
|
$rcN = mb_strtoupper($rc);
|
|
|
|
|
|
|
|
|
|
if ($rcN !== '' && $ruN === $rcN) {
|
|
|
|
|
$nuevoEsCorrecta = 1;
|
|
|
|
|
$nuevoPuntaje = $puntosCorrecta;
|
|
|
|
|
$totalCorrectas++;
|
|
|
|
|
$correctasPorCurso[$curso]++;
|
|
|
|
|
} else {
|
|
|
|
|
$nuevoEsCorrecta = 0;
|
|
|
|
|
$nuevoPuntaje = $puntosIncorrecta;
|
|
|
|
|
$totalIncorrectas++;
|
|
|
|
|
$incorrectasPorCurso[$curso]++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$totalPuntos += (float) $nuevoPuntaje;
|
|
|
|
|
|
|
|
|
|
DB::table('preguntas_asignadas')
|
|
|
|
|
->where('id', $row->pa_id)
|
|
|
|
|
->update([
|
|
|
|
|
'es_correcta' => $nuevoEsCorrecta,
|
|
|
|
|
'puntaje' => $nuevoPuntaje,
|
|
|
|
|
'updated_at' => now(),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 6) Resumen
|
|
|
|
|
$totalPreguntas = (int) $items->count();
|
|
|
|
|
$porcentajeCorrectas = $totalPreguntas > 0 ? ($totalCorrectas / $totalPreguntas) * 100 : 0;
|
|
|
|
|
|
|
|
|
|
$calificacionSobre20 = ($puntajeMaximo > 0)
|
|
|
|
|
? ($totalPuntos / $puntajeMaximo) * 20
|
|
|
|
|
: 0;
|
|
|
|
|
|
|
|
|
|
$correctasPorCursoFormato = [];
|
|
|
|
|
foreach ($correctasPorCurso as $curso => $corr) {
|
|
|
|
|
$y = $preguntasTotalesPorCurso[$curso] ?? 0;
|
|
|
|
|
$correctasPorCursoFormato[$curso] = "{$corr} de {$y}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 7) Guardar resultado en resultados_examenes
|
|
|
|
|
$resultadoId = DB::table('resultados_examenes')->insertGetId([
|
|
|
|
|
'postulante_id' => $postulante->id,
|
|
|
|
|
'examen_id' => $examen->id,
|
|
|
|
|
'total_puntos' => round($totalPuntos, 3),
|
|
|
|
|
'correctas_por_curso' => json_encode($correctasPorCursoFormato),
|
|
|
|
|
'incorrectas_por_curso' => json_encode($incorrectasPorCurso),
|
|
|
|
|
'preguntas_totales_por_curso' => json_encode($preguntasTotalesPorCurso),
|
|
|
|
|
'total_correctas' => $totalCorrectas,
|
|
|
|
|
'total_incorrectas' => $totalIncorrectas,
|
|
|
|
|
'total_nulas' => $totalNulas,
|
|
|
|
|
'porcentaje_correctas' => round($porcentajeCorrectas, 2),
|
|
|
|
|
'calificacion_sobre_20' => round($calificacionSobre20, 2),
|
|
|
|
|
'created_at' => now(),
|
|
|
|
|
'updated_at' => now(),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
// 8) Recalcular orden de mérito por proceso
|
|
|
|
|
$this->recalcularOrdenMerito($cfg->proceso_id);
|
|
|
|
|
|
|
|
|
|
// 9) Leer orden_merito ya asignado (opcional)
|
|
|
|
|
$orden = DB::table('resultados_examenes')->where('id', $resultadoId)->value('orden_merito');
|
|
|
|
|
|
|
|
|
|
DB::table('examenes')->where('id', $examen->id)->update([
|
|
|
|
|
'estado' => 'calificado',
|
|
|
|
|
'hora_fin' => now(),
|
|
|
|
|
]);
|
|
|
|
|
return response()->json([
|
|
|
|
|
'success' => true,
|
|
|
|
|
'mensaje' => 'Examen calificado exitosamente.',
|
|
|
|
|
'examen_id' => $examen->id,
|
|
|
|
|
'proceso_id' => $cfg->proceso_id,
|
|
|
|
|
|
|
|
|
|
'total_puntos' => round($totalPuntos, 2),
|
|
|
|
|
'total_correctas' => $totalCorrectas,
|
|
|
|
|
'total_incorrectas' => $totalIncorrectas,
|
|
|
|
|
'total_nulas' => $totalNulas,
|
|
|
|
|
|
|
|
|
|
'porcentaje_correctas' => round($porcentajeCorrectas, 2),
|
|
|
|
|
'calificacion_sobre_20' => round($calificacionSobre20, 2),
|
|
|
|
|
'orden_merito' => $orden,
|
|
|
|
|
|
|
|
|
|
'correctas_por_curso' => $correctasPorCursoFormato,
|
|
|
|
|
'incorrectas_por_curso' => $incorrectasPorCurso,
|
|
|
|
|
'preguntas_totales_por_curso' => $preguntasTotalesPorCurso,
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function recalcularOrdenMerito($procesoId): void
|
|
|
|
|
{
|
|
|
|
|
DB::statement("
|
|
|
|
|
UPDATE resultados_examenes r
|
|
|
|
|
JOIN (
|
|
|
|
|
SELECT
|
|
|
|
|
r2.id,
|
|
|
|
|
ROW_NUMBER() OVER (
|
|
|
|
|
ORDER BY
|
|
|
|
|
r2.total_puntos DESC,
|
|
|
|
|
COALESCE(r2.updated_at, r2.created_at) ASC
|
|
|
|
|
) AS nuevo_orden
|
|
|
|
|
FROM resultados_examenes r2
|
|
|
|
|
JOIN examenes e ON e.id = r2.examen_id
|
|
|
|
|
JOIN area_proceso ap ON ap.id = e.area_proceso_id
|
|
|
|
|
WHERE ap.proceso_id = ?
|
|
|
|
|
) x ON x.id = r.id
|
|
|
|
|
SET r.orden_merito = x.nuevo_orden
|
|
|
|
|
", [$procesoId]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|