main
elmer-20 2 days ago
parent e6343ce179
commit a0cb4ae2ee

@ -1,5 +1,5 @@
# Usa Maven y JDK para compilar y correr código
FROM maven:3.9.2-eclipse-temurin-17
FROM eclipse-temurin:25-jdk
WORKDIR /app

@ -0,0 +1,31 @@
package com.service.ingresantes.controller;
import com.service.ingresantes.service.CalificacionCursoService;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/calificacion")
public class CalificacionController {
private final CalificacionCursoService calificacionCursoService;
public CalificacionController(CalificacionCursoService calificacionCursoService) {
this.calificacionCursoService = calificacionCursoService;
}
@PostMapping("/calificar/{procesoId}")
public Map<String, Object> calificarCursos(@PathVariable Long procesoId) {
int registros = calificacionCursoService.calificarCursos(procesoId);
Map<String, Object> response = new HashMap<>();
response.put("mensaje", "Calificación por asignaturas completada");
response.put("proceso", procesoId);
response.put("registros_generados", registros);
return response;
}
}

@ -7,6 +7,7 @@ import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/api/excel")
@CrossOrigin(origins = "*")
public class ExcelController {
private final ExcelService excelService;
@ -17,16 +18,24 @@ public class ExcelController {
@PostMapping("/upload")
public ResponseEntity<String> uploadExcel(@RequestParam("file") MultipartFile file) {
try {
excelService.importarExcel(file);
return ResponseEntity.ok("Archivo subido y procesado correctamente!");
if (file == null || file.isEmpty()) {
return ResponseEntity.badRequest().body("No se recibió ningún archivo.");
}
String resultado = excelService.importarExcel(file);
return ResponseEntity.ok(resultado);
} catch (Exception e) {
e.printStackTrace();
return ResponseEntity.badRequest().body("Error al procesar el archivo: " + e.getMessage());
}
}
@GetMapping("/ping")
public String ping() {
return "API funcionando123456!";
public ResponseEntity<String> ping() {
return ResponseEntity.ok("API funcionando correctamente");
}
}

@ -18,22 +18,28 @@ public class ExcelResultadoController {
@PostMapping("/upload-resultados")
public ResponseEntity<String> uploadResultados(
@RequestParam("file") MultipartFile file) {
@RequestParam("file") MultipartFile file,
@RequestParam("dia") Integer dia) {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("El archivo está vacío");
}
try {
if (dia == null || dia < 1) {
return ResponseEntity.badRequest()
.body("El parámetro 'dia' es requerido y debe ser >= 1");
}
excelResultadoService.importarExcelResultados(file);
try {
// 🔥 AQUÍ CAPTURAS EL RESUMEN
String resultado = excelResultadoService.importarExcelResultados(file, dia);
return ResponseEntity.ok("Archivo de resultados procesado correctamente");
return ResponseEntity.ok(
"Archivo del día " + dia + " procesado correctamente\n\n" + resultado
);
} catch (Exception e) {
return ResponseEntity
.badRequest()
return ResponseEntity.badRequest()
.body("Error al procesar el archivo: " + e.getMessage());
}
}

@ -19,20 +19,21 @@ public class ResultadoController {
this.resultadoService = resultadoService;
}
// Buscar resultado por DNI y proceso
@GetMapping("/{dni}/{procesoId}")
// Buscar resultado por DNI, proceso y día
@GetMapping("/{dni}/{procesoId}/{dia}")
public ResponseEntity<?> obtenerResultado(
@PathVariable String dni,
@PathVariable Long procesoId) {
@PathVariable Long procesoId,
@PathVariable Integer dia) {
Optional<ResultadoExamen> resultado =
resultadoService.obtenerResultado(dni, procesoId);
resultadoService.obtenerResultado(dni, procesoId, dia);
if (resultado.isEmpty()) {
return ResponseEntity
.badRequest()
.body("No se encontró resultado para ese DNI y proceso");
.body("No se encontró resultado para ese DNI, proceso y día");
}
return ResponseEntity.ok(resultado.get());

@ -30,4 +30,6 @@ public class ClaveExamen {
@ManyToOne
@JoinColumn(name = "area_id")
private Area area;
}

@ -53,4 +53,6 @@ public class Postulante {
@Column(name = "distrito_colegio")
private String distritoColegio;
}
}

@ -0,0 +1,44 @@
package com.service.ingresantes.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "resultado_asignatura")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ResultadoAsignatura {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// resultado del examen
@ManyToOne
@JoinColumn(name = "resultado_examen_id", nullable = false)
private ResultadoExamen resultadoExamen;
// proceso
@ManyToOne
@JoinColumn(name = "proceso_id", nullable = false)
private Proceso proceso;
// área
@ManyToOne
@JoinColumn(name = "area_id", nullable = false)
private Area area;
// curso
@ManyToOne
@JoinColumn(name = "asignatura_id", nullable = false)
private Asignatura asignatura;
// estadísticas
private Integer correctas;
private Integer incorrectas;
private Integer blanco;
private Double puntaje;
}

@ -8,7 +8,7 @@ import lombok.Data;
@Table(
name = "resultado_examen",
uniqueConstraints = {
@UniqueConstraint(columnNames = {"inscripcion_id"})
@UniqueConstraint(columnNames = {"inscripcion_id", "dia"})
}
)
public class ResultadoExamen {
@ -17,7 +17,7 @@ public class ResultadoExamen {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@ManyToOne
@JoinColumn(name = "inscripcion_id", nullable = false)
private Inscripcion inscripcion;
@ -25,10 +25,16 @@ public class ResultadoExamen {
@JoinColumn(name = "proceso_id", nullable = false)
private Proceso proceso;
@Column(nullable = false)
private Integer dia;
private Double puntaje;
@Column(name = "vocacional", nullable = true)
private Double vocacional; // ← NUEVO, puede ser null
@Column(length = 2)
private String apto; // SI / NO / CL
private String apto;
private String obs;

@ -2,10 +2,11 @@ package com.service.ingresantes.repository;
import com.service.ingresantes.entity.Asignatura;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
import java.util.List;
public interface AsignaturaRepository extends JpaRepository<Asignatura, Long> {
// Aquí puedes agregar consultas personalizadas si lo necesitas
// Ejemplo: List<Asignatura> findByNombre(String nombre);
List<Asignatura> findByAreaIdOrderByCodigo(Long areaId);
}

@ -11,5 +11,10 @@ public interface ClaveExamenRepository extends JpaRepository<ClaveExamen, Long>
// Busca una clave por proceso, área e idExamen
Optional<ClaveExamen> findByProcesoIdAndAreaIdAndIdExamen(Long procesoId, Long areaId, String idExamen);
Optional<ClaveExamen> findByProcesoIdAndAreaIdAndTipo(
Long procesoId,
Long areaId,
String tipo
);
}

@ -0,0 +1,10 @@
package com.service.ingresantes.repository;
import com.service.ingresantes.entity.ResultadoAsignatura;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ResultadoAsignaturaRepository extends JpaRepository<ResultadoAsignatura, Long> {
boolean existsByProcesoId(Long procesoId);
}

@ -9,20 +9,18 @@ import java.util.Optional;
public interface ResultadoExamenRepository extends JpaRepository<ResultadoExamen, Long> {
boolean existsByInscripcionId(Long inscripcionId);
boolean existsByInscripcionIdAndDia(Long inscripcionId, Integer dia);
// Buscar resultado por DNI y proceso
@Query("""
SELECT r FROM ResultadoExamen r
JOIN r.inscripcion i
JOIN i.postulante p
WHERE p.dni = :dni
AND i.proceso.id = :procesoId
AND r.dia = :dia
""")
Optional<ResultadoExamen> findByDniAndProceso(String dni, Long procesoId);
Optional<ResultadoExamen> findByDniAndProcesoAndDia(String dni, Long procesoId, Integer dia);
// Ranking por proceso
@Query("""
SELECT r FROM ResultadoExamen r
JOIN r.inscripcion i
@ -30,4 +28,6 @@ public interface ResultadoExamenRepository extends JpaRepository<ResultadoExamen
ORDER BY r.puntaje DESC
""")
List<ResultadoExamen> findRankingByProceso(Long procesoId);
List<ResultadoExamen> findByProcesoId(Long procesoId);
}

@ -0,0 +1,128 @@
package com.service.ingresantes.service;
import com.service.ingresantes.entity.*;
import com.service.ingresantes.repository.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
@Service
@Transactional
public class CalificacionCursoService {
private final ResultadoExamenRepository resultadoRepo;
private final ClaveExamenRepository claveRepo;
private final AsignaturaRepository asignaturaRepo;
private final ResultadoAsignaturaRepository resultadoAsignaturaRepo;
public CalificacionCursoService(
ResultadoExamenRepository resultadoRepo,
ClaveExamenRepository claveRepo,
AsignaturaRepository asignaturaRepo,
ResultadoAsignaturaRepository resultadoAsignaturaRepo) {
this.resultadoRepo = resultadoRepo;
this.claveRepo = claveRepo;
this.asignaturaRepo = asignaturaRepo;
this.resultadoAsignaturaRepo = resultadoAsignaturaRepo;
}
public int calificarCursos(Long procesoId) {
if (resultadoAsignaturaRepo.existsByProcesoId(procesoId)) {
throw new RuntimeException("Este proceso ya fue calificado por cursos");
}
List<ResultadoExamen> resultados = resultadoRepo.findByProcesoId(procesoId);
List<ResultadoAsignatura> listaGuardar = new ArrayList<>();
for (ResultadoExamen resultado : resultados) {
String respuestas = resultado.getRespuestas();
if (respuestas == null || respuestas.isEmpty())
continue;
Long areaId = Long.parseLong(resultado.getIdExamen());
String tipo = resultado.getTipo();
Optional<ClaveExamen> claveOpt =
claveRepo.findByProcesoIdAndAreaIdAndTipo(
procesoId,
areaId,
tipo
);
if (claveOpt.isEmpty())
continue;
String clave = claveOpt.get().getClave();
List<Asignatura> asignaturas =
asignaturaRepo.findByAreaIdOrderByCodigo(areaId);
int pos = 0;
for (Asignatura asignatura : asignaturas) {
int preguntas = asignatura.getCantidadPreguntas();
int fin = pos + preguntas;
if (fin > respuestas.length())
break;
String resp = respuestas.substring(pos, fin);
String cla = clave.substring(pos, fin);
int correctas = 0;
int incorrectas = 0;
int blanco = 0;
for (int i = 0; i < resp.length(); i++) {
char r = resp.charAt(i);
char c = cla.charAt(i);
if (r == ' ' || r == '0') {
blanco++;
}
else if (r == c) {
correctas++;
}
else {
incorrectas++;
}
}
double ponderacion = asignatura.getPonderacion();
double puntaje =
(correctas * 10 * ponderacion) +
(blanco * 2 * ponderacion);
ResultadoAsignatura resultadoAsignatura = ResultadoAsignatura.builder()
.resultadoExamen(resultado)
.proceso(resultado.getProceso())
.area(asignatura.getArea())
.asignatura(asignatura)
.correctas(correctas)
.incorrectas(incorrectas)
.blanco(blanco)
.puntaje(puntaje)
.build();
listaGuardar.add(resultadoAsignatura);
pos = fin;
}
}
resultadoAsignaturaRepo.saveAll(listaGuardar);
return listaGuardar.size();
}
}

@ -10,8 +10,9 @@ import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import jakarta.transaction.Transactional;
import java.io.InputStream;
import java.util.Optional;
import java.util.*;
@Service
public class ExcelResultadoService {
@ -22,141 +23,201 @@ public class ExcelResultadoService {
public ExcelResultadoService(
InscripcionRepository inscripcionRepo,
ResultadoExamenRepository resultadoRepo) {
this.inscripcionRepo = inscripcionRepo;
this.resultadoRepo = resultadoRepo;
}
private String getCellString(Cell cell) {
if (cell == null) return "";
switch (cell.getCellType()) {
case STRING:
return cell.getStringCellValue().trim();
case NUMERIC:
return String.valueOf((long) cell.getNumericCellValue());
default:
return "";
}
}
private Double getCellDouble(Cell cell) {
if (cell == null) return null;
if (cell.getCellType() == CellType.NUMERIC) {
return cell.getNumericCellValue();
}
if (cell.getCellType() == CellType.NUMERIC) return cell.getNumericCellValue();
if (cell.getCellType() == CellType.STRING) {
return Double.parseDouble(cell.getStringCellValue());
try {
return Double.parseDouble(cell.getStringCellValue());
} catch (Exception e) {
return null;
}
}
return null;
}
private Integer getCellInteger(Cell cell) {
if (cell == null) return null;
if (cell.getCellType() == CellType.NUMERIC) {
return (int) cell.getNumericCellValue();
}
if (cell.getCellType() == CellType.NUMERIC) return (int) cell.getNumericCellValue();
if (cell.getCellType() == CellType.STRING) {
return Integer.parseInt(cell.getStringCellValue());
try {
return Integer.parseInt(cell.getStringCellValue());
} catch (Exception e) {
return null;
}
}
return null;
}
@Transactional
public void importarExcelResultados(MultipartFile file) throws Exception {
InputStream is = file.getInputStream();
Workbook workbook = WorkbookFactory.create(is);
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
if (row.getRowNum() == 0) continue;
try {
String dni = getCellString(row.getCell(0));
String procesoStr = getCellString(row.getCell(1));
if (dni.isEmpty() || procesoStr.isEmpty()) {
continue;
}
Long procesoId = Long.parseLong(procesoStr);
Optional<Inscripcion> inscripcionOpt =
inscripcionRepo.findByPostulanteDniAndProcesoId(dni, procesoId);
if (inscripcionOpt.isEmpty()) {
System.out.println("No existe inscripción -> DNI " + dni);
continue;
}
Inscripcion inscripcion = inscripcionOpt.get();
if (resultadoRepo.existsByInscripcionId(inscripcion.getId())) {
private void incrementar(Map<String, Integer> mapa, String clave) {
mapa.put(clave, mapa.getOrDefault(clave, 0) + 1);
}
System.out.println("Resultado ya existe -> DNI " + dni);
continue;
@Transactional
public String importarExcelResultados(MultipartFile file, Integer dia) throws Exception {
int totalFilas = 0;
int guardados = 0;
int omitidos = 0;
int errores = 0;
int dniVacio = 0;
int procesoVacio = 0;
int inscripcionNoExiste = 0;
int duplicado = 0;
Map<String, Integer> omitidasPorCausa = new TreeMap<>();
// 🔥 NUEVO: lista de detalles de omitidos
List<String> detalleOmitidos = new ArrayList<>();
try (InputStream is = file.getInputStream();
Workbook workbook = WorkbookFactory.create(is)) {
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
if (row.getRowNum() == 0) continue;
totalFilas++;
try {
String dni = getCellString(row.getCell(0));
String procesoStr = getCellString(row.getCell(1));
if (dni.isEmpty()) {
omitidos++;
dniVacio++;
detalleOmitidos.add("Fila " + (row.getRowNum()+1) + " -> DNI vacío");
incrementar(omitidasPorCausa, "DNI vacío");
continue;
}
if (procesoStr.isEmpty()) {
omitidos++;
procesoVacio++;
detalleOmitidos.add("DNI: " + dni + " -> Proceso vacío");
incrementar(omitidasPorCausa, "Proceso vacío");
continue;
}
Long procesoId;
try {
procesoId = Long.parseLong(procesoStr);
} catch (Exception e) {
omitidos++;
procesoVacio++;
detalleOmitidos.add("DNI: " + dni + " -> Proceso inválido: " + procesoStr);
incrementar(omitidasPorCausa, "Proceso inválido");
continue;
}
Optional<Inscripcion> inscripcionOpt =
inscripcionRepo.findByPostulanteDniAndProcesoId(dni, procesoId);
if (inscripcionOpt.isEmpty()) {
omitidos++;
inscripcionNoExiste++;
detalleOmitidos.add("DNI: " + dni + " | Proceso: " + procesoId + " -> NO EXISTE INSCRIPCIÓN");
incrementar(omitidasPorCausa, "Inscripción no existe");
continue;
}
Inscripcion inscripcion = inscripcionOpt.get();
if (resultadoRepo.existsByInscripcionIdAndDia(inscripcion.getId(), dia)) {
omitidos++;
duplicado++;
detalleOmitidos.add("DNI: " + dni + " | Proceso: " + procesoId + " -> DUPLICADO");
incrementar(omitidasPorCausa, "Resultado duplicado");
continue;
}
Double puntaje = getCellDouble(row.getCell(2));
Double vocacional = getCellDouble(row.getCell(3));
String apto = getCellString(row.getCell(4));
String obs = getCellString(row.getCell(5));
String idExamen = getCellString(row.getCell(6));
String litho = getCellString(row.getCell(7));
String numLectura = getCellString(row.getCell(8));
String tipo = getCellString(row.getCell(9));
String calificar = getCellString(row.getCell(10));
String aula = getCellString(row.getCell(11));
String respuestas = getCellString(row.getCell(12));
Integer puesto = getCellInteger(row.getCell(13));
ResultadoExamen resultado = new ResultadoExamen();
resultado.setInscripcion(inscripcion);
resultado.setProceso(inscripcion.getProceso());
resultado.setDia(dia);
resultado.setPuntaje(puntaje);
resultado.setVocacional(vocacional);
resultado.setApto(apto);
resultado.setObs(obs);
resultado.setIdExamen(idExamen);
resultado.setLitho(litho);
resultado.setNumLectura(numLectura);
resultado.setTipo(tipo);
resultado.setCalificar(calificar);
resultado.setAula(aula);
resultado.setRespuestas(respuestas);
resultado.setPuesto(puesto);
resultadoRepo.save(resultado);
guardados++;
} catch (Exception e) {
errores++;
detalleOmitidos.add("Fila " + (row.getRowNum()+1) + " -> ERROR: " + e.getMessage());
incrementar(omitidasPorCausa, "Error interno");
}
}
}
Double puntaje = getCellDouble(row.getCell(2));
String apto = getCellString(row.getCell(3));
String obs = getCellString(row.getCell(4));
String idExamen = getCellString(row.getCell(5));
String litho = getCellString(row.getCell(6));
String numLectura = getCellString(row.getCell(7));
String tipo = getCellString(row.getCell(8));
String calificar = getCellString(row.getCell(9));
String aula = getCellString(row.getCell(10));
String respuestas = getCellString(row.getCell(11));
Integer puesto = getCellInteger(row.getCell(12));
ResultadoExamen resultado = new ResultadoExamen();
resultado.setInscripcion(inscripcion);
resultado.setProceso(inscripcion.getProceso());
resultado.setPuntaje(puntaje);
resultado.setApto(apto);
resultado.setObs(obs);
resultado.setIdExamen(idExamen);
resultado.setLitho(litho);
resultado.setNumLectura(numLectura);
resultado.setTipo(tipo);
resultado.setCalificar(calificar);
resultado.setAula(aula);
resultado.setRespuestas(respuestas);
resultado.setPuesto(puesto);
StringBuilder sb = new StringBuilder();
resultadoRepo.save(resultado);
sb.append("IMPORTACIÓN RESULTADOS\n");
sb.append("Total filas: ").append(totalFilas).append("\n");
sb.append("Guardados: ").append(guardados).append("\n");
sb.append("Omitidos: ").append(omitidos).append("\n");
sb.append("Errores: ").append(errores).append("\n\n");
System.out.println("Resultado guardado -> DNI " + dni);
sb.append("DETALLE:\n");
sb.append("- DNI vacío: ").append(dniVacio).append("\n");
sb.append("- Proceso vacío/inválido: ").append(procesoVacio).append("\n");
sb.append("- Inscripción no existe: ").append(inscripcionNoExiste).append("\n");
sb.append("- Resultado duplicado: ").append(duplicado).append("\n\n");
} catch (Exception e) {
sb.append("CAUSAS AGRUPADAS:\n");
for (Map.Entry<String, Integer> entry : omitidasPorCausa.entrySet()) {
sb.append("- ").append(entry.getKey()).append(": ")
.append(entry.getValue()).append("\n");
}
System.out.println("Error fila " + row.getRowNum() + " -> " + e.getMessage());
}
// 🔥 NUEVO BLOQUE
sb.append("\nDETALLE DE DNIs OMITIDOS:\n");
for (String d : detalleOmitidos) {
sb.append("- ").append(d).append("\n");
}
workbook.close();
is.close();
return sb.toString();
}
}

@ -1,17 +1,28 @@
package com.service.ingresantes.service;
import com.service.ingresantes.entity.*;
import com.service.ingresantes.repository.*;
import com.service.ingresantes.entity.Inscripcion;
import com.service.ingresantes.entity.Modalidad;
import com.service.ingresantes.entity.Postulante;
import com.service.ingresantes.entity.Proceso;
import com.service.ingresantes.entity.ProgramaEstudio;
import com.service.ingresantes.repository.InscripcionRepository;
import com.service.ingresantes.repository.ModalidadRepository;
import com.service.ingresantes.repository.PostulanteRepository;
import com.service.ingresantes.repository.ProcesoRepository;
import com.service.ingresantes.repository.ProgramaRepository;
import jakarta.transaction.Transactional;
import org.apache.poi.ss.usermodel.*;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import jakarta.transaction.Transactional;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.ZoneId;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
@Service
public class ExcelService {
@ -34,157 +45,388 @@ public class ExcelService {
this.inscripcionRepo = inscripcionRepo;
}
// --- Método auxiliar para leer cualquier celda como String ---
private String getCellStringValue(Cell cell) {
if (cell == null) return "";
switch (cell.getCellType()) {
case STRING:
return cell.getStringCellValue().trim();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
return cell.getLocalDateTimeCellValue().toLocalDate().toString();
} else {
return String.valueOf((long) cell.getNumericCellValue());
try {
return switch (cell.getCellType()) {
case STRING -> cell.getStringCellValue().trim();
case NUMERIC -> {
if (DateUtil.isCellDateFormatted(cell)) {
yield cell.getLocalDateTimeCellValue().toLocalDate().toString();
} else {
double value = cell.getNumericCellValue();
long longValue = (long) value;
if (value == longValue) {
yield String.valueOf(longValue);
} else {
yield String.valueOf(value);
}
}
}
case BOOLEAN -> String.valueOf(cell.getBooleanCellValue());
case FORMULA -> {
try {
FormulaEvaluator evaluator = cell.getSheet()
.getWorkbook()
.getCreationHelper()
.createFormulaEvaluator();
CellValue cellValue = evaluator.evaluate(cell);
yield switch (cellValue.getCellType()) {
case STRING -> cellValue.getStringValue().trim();
case NUMERIC -> {
double value = cellValue.getNumberValue();
long longValue = (long) value;
if (value == longValue) {
yield String.valueOf(longValue);
} else {
yield String.valueOf(value);
}
}
case BOOLEAN -> String.valueOf(cellValue.getBooleanValue());
default -> "";
};
} catch (Exception e) {
yield "";
}
}
case BOOLEAN:
return String.valueOf(cell.getBooleanCellValue());
case FORMULA:
return cell.getCellFormula();
default:
return "";
case BLANK -> "";
default -> "";
};
} catch (Exception e) {
return "";
}
}
@Transactional
public void importarExcel(MultipartFile file) throws Exception {
InputStream is = file.getInputStream();
Workbook workbook = WorkbookFactory.create(is);
Sheet sheet = workbook.getSheetAt(0);
private LocalDate getCellLocalDate(Cell cell) {
if (cell == null) return null;
DateTimeFormatter fechaHoraFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
try {
if (cell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell)) {
return cell.getDateCellValue()
.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
}
for (Row row : sheet) {
if (row.getRowNum() == 0) continue; // Saltar encabezado
String value = getCellStringValue(cell);
if (value == null || value.isBlank()) return null;
String dni = getCellStringValue(row.getCell(0));
if (dni.isEmpty()) {
System.out.println("Fila " + row.getRowNum() + ": DNI vacío, se omite");
continue;
return LocalDate.parse(value.trim());
} catch (Exception e) {
return null;
}
}
private LocalDateTime getCellLocalDateTime(Cell cell) {
if (cell == null) return null;
try {
if (cell.getCellType() == CellType.NUMERIC && DateUtil.isCellDateFormatted(cell)) {
return cell.getDateCellValue()
.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
}
Postulante postulante = postulanteRepo.findById(dni).orElseGet(() -> {
String value = getCellStringValue(cell);
if (value == null || value.isBlank()) return null;
try {
return LocalDateTime.parse(value.trim().replace(" ", "T"));
} catch (Exception e) {
try {
return LocalDate.parse(value.trim()).atStartOfDay();
} catch (Exception ex) {
return null;
}
}
} catch (Exception e) {
return null;
}
}
private Integer parseInteger(String value) {
try {
if (value == null || value.isBlank()) return null;
return Integer.parseInt(value.trim());
} catch (Exception e) {
return null;
}
}
private Long parseLong(String value) {
try {
if (value == null || value.isBlank()) return null;
return Long.parseLong(value.trim());
} catch (Exception e) {
return null;
}
}
private void incrementar(Map<String, Integer> mapa, String clave) {
mapa.put(clave, mapa.getOrDefault(clave, 0) + 1);
}
@Transactional
public String importarExcel(MultipartFile file) throws Exception {
int totalFilas = 0;
int postulantesCreados = 0;
int inscripcionesGuardadas = 0;
int filasOmitidas = 0;
int errores = 0;
int dniVacio = 0;
int procesoVacio = 0;
int procesoInvalido = 0;
int procesoNoEncontrado = 0;
int programaInvalido = 0;
int programaNoEncontrado = 0;
int modalidadInvalida = 0;
int modalidadNoEncontrada = 0;
int inscripcionDuplicada = 0;
Map<String, Integer> omitidasPorProceso = new TreeMap<>();
Map<String, Integer> omitidasPorPrograma = new TreeMap<>();
Map<String, Integer> omitidasPorModalidad = new TreeMap<>();
Map<String, Integer> omitidasPorProcesoModalidad = new TreeMap<>();
Map<String, Integer> omitidasPorCausa = new TreeMap<>();
try (InputStream is = file.getInputStream();
Workbook workbook = WorkbookFactory.create(is)) {
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
if (row.getRowNum() == 0) continue;
totalFilas++;
String dni = getCellStringValue(row.getCell(0));
String procesoStr = getCellStringValue(row.getCell(18));
String programaStr = getCellStringValue(row.getCell(19));
String modalidadStr = getCellStringValue(row.getCell(20));
String procesoKey = procesoStr.isBlank() ? "SIN_PROCESO" : "Proceso " + procesoStr;
String programaKey = programaStr.isBlank() ? "SIN_PROGRAMA" : "Programa " + programaStr;
String modalidadKey = modalidadStr.isBlank() ? "SIN_MODALIDAD" : "Modalidad " + modalidadStr;
String procesoModalidadKey = procesoKey + " | " + modalidadKey;
try {
Postulante p = new Postulante();
p.setDni(dni);
p.setPaterno(getCellStringValue(row.getCell(1)));
p.setMaterno(getCellStringValue(row.getCell(2)));
p.setNombres(getCellStringValue(row.getCell(3)));
p.setSexo(getCellStringValue(row.getCell(4)));
String fechaNacStr = getCellStringValue(row.getCell(5));
if (!fechaNacStr.isEmpty()) {
try { p.setFechaNacimiento(LocalDate.parse(fechaNacStr)); } catch (Exception e) {}
if (dni.isBlank()) {
filasOmitidas++;
dniVacio++;
incrementar(omitidasPorProceso, procesoKey);
incrementar(omitidasPorPrograma, programaKey);
incrementar(omitidasPorModalidad, modalidadKey);
incrementar(omitidasPorProcesoModalidad, procesoModalidadKey);
incrementar(omitidasPorCausa, "DNI vacío");
continue;
}
Postulante postulante = postulanteRepo.findById(dni).orElse(null);
if (postulante == null) {
postulante = new Postulante();
postulante.setDni(dni);
postulante.setPaterno(getCellStringValue(row.getCell(1)));
postulante.setMaterno(getCellStringValue(row.getCell(2)));
postulante.setNombres(getCellStringValue(row.getCell(3)));
postulante.setSexo(getCellStringValue(row.getCell(4)));
LocalDate fechaNacimiento = getCellLocalDate(row.getCell(5));
if (fechaNacimiento != null) postulante.setFechaNacimiento(fechaNacimiento);
Integer edad = parseInteger(getCellStringValue(row.getCell(6)));
if (edad != null) postulante.setEdad(edad);
postulante.setUbigeoResidencia(getCellStringValue(row.getCell(7)));
postulante.setDepartamentoResidencia(getCellStringValue(row.getCell(8)));
postulante.setProvinciaResidencia(getCellStringValue(row.getCell(9)));
postulante.setDistritoResidencia(getCellStringValue(row.getCell(10)));
Integer egreso = parseInteger(getCellStringValue(row.getCell(11)));
if (egreso != null) postulante.setEgreso(egreso);
postulante.setCodModular(getCellStringValue(row.getCell(12)));
postulante.setUbigeoColegio(getCellStringValue(row.getCell(13)));
postulante.setDepartamentoColegio(getCellStringValue(row.getCell(14)));
postulante.setProvinciaColegio(getCellStringValue(row.getCell(15)));
postulante.setDistritoColegio(getCellStringValue(row.getCell(16)));
postulante = postulanteRepo.save(postulante);
postulantesCreados++;
}
String edadStr = getCellStringValue(row.getCell(6));
if (!edadStr.isEmpty()) {
try { p.setEdad(Integer.parseInt(edadStr)); } catch (NumberFormatException ignored) {}
LocalDateTime fechaInscripcion = getCellLocalDateTime(row.getCell(17));
if (fechaInscripcion == null) {
fechaInscripcion = LocalDateTime.now();
}
p.setUbigeoResidencia(getCellStringValue(row.getCell(7)));
p.setDepartamentoResidencia(getCellStringValue(row.getCell(8)));
p.setProvinciaResidencia(getCellStringValue(row.getCell(9)));
p.setDistritoResidencia(getCellStringValue(row.getCell(10)));
if (procesoStr.isBlank()) {
filasOmitidas++;
procesoVacio++;
incrementar(omitidasPorProceso, procesoKey);
incrementar(omitidasPorPrograma, programaKey);
incrementar(omitidasPorModalidad, modalidadKey);
incrementar(omitidasPorProcesoModalidad, procesoModalidadKey);
incrementar(omitidasPorCausa, "Proceso vacío");
continue;
}
String egresoStr = getCellStringValue(row.getCell(11));
if (!egresoStr.isEmpty()) {
try { p.setEgreso(Integer.parseInt(egresoStr)); } catch (NumberFormatException ignored) {}
Long procesoId = parseLong(procesoStr);
if (procesoId == null) {
filasOmitidas++;
procesoInvalido++;
incrementar(omitidasPorProceso, procesoKey);
incrementar(omitidasPorPrograma, programaKey);
incrementar(omitidasPorModalidad, modalidadKey);
incrementar(omitidasPorProcesoModalidad, procesoModalidadKey);
incrementar(omitidasPorCausa, "Proceso inválido");
continue;
}
p.setCodModular(getCellStringValue(row.getCell(12)));
p.setUbigeoColegio(getCellStringValue(row.getCell(13)));
p.setDepartamentoColegio(getCellStringValue(row.getCell(14)));
p.setProvinciaColegio(getCellStringValue(row.getCell(15)));
p.setDistritoColegio(getCellStringValue(row.getCell(16)));
Optional<Proceso> procesoOpt = procesoRepo.findById(procesoId);
if (procesoOpt.isEmpty()) {
filasOmitidas++;
procesoNoEncontrado++;
incrementar(omitidasPorProceso, procesoKey);
incrementar(omitidasPorPrograma, programaKey);
incrementar(omitidasPorModalidad, modalidadKey);
incrementar(omitidasPorProcesoModalidad, procesoModalidadKey);
incrementar(omitidasPorCausa, "Proceso no encontrado");
continue;
}
Proceso proceso = procesoOpt.get();
System.out.println("Fila " + row.getRowNum() + ": Postulante creado -> " + dni);
return postulanteRepo.save(p);
} catch (Exception e) {
System.out.println("Fila " + row.getRowNum() + ": Error guardando postulante " + dni + " -> " + e.getMessage());
return null;
}
});
Long programaId = parseLong(programaStr);
if (programaId == null) {
filasOmitidas++;
programaInvalido++;
incrementar(omitidasPorProceso, procesoKey);
incrementar(omitidasPorPrograma, programaKey);
incrementar(omitidasPorModalidad, modalidadKey);
incrementar(omitidasPorProcesoModalidad, procesoModalidadKey);
incrementar(omitidasPorCausa, "Programa inválido");
continue;
}
if (postulante == null) continue;
Optional<ProgramaEstudio> programaOpt = programaRepo.findById(programaId);
if (programaOpt.isEmpty()) {
filasOmitidas++;
programaNoEncontrado++;
incrementar(omitidasPorProceso, procesoKey);
incrementar(omitidasPorPrograma, programaKey);
incrementar(omitidasPorModalidad, modalidadKey);
incrementar(omitidasPorProcesoModalidad, procesoModalidadKey);
incrementar(omitidasPorCausa, "Programa no encontrado");
continue;
}
ProgramaEstudio programa = programaOpt.get();
Long modalidadId = parseLong(modalidadStr);
if (modalidadId == null) {
filasOmitidas++;
modalidadInvalida++;
incrementar(omitidasPorProceso, procesoKey);
incrementar(omitidasPorPrograma, programaKey);
incrementar(omitidasPorModalidad, modalidadKey);
incrementar(omitidasPorProcesoModalidad, procesoModalidadKey);
incrementar(omitidasPorCausa, "Modalidad inválida");
continue;
}
Optional<Modalidad> modalidadOpt = modalidadRepo.findById(modalidadId);
if (modalidadOpt.isEmpty()) {
filasOmitidas++;
modalidadNoEncontrada++;
incrementar(omitidasPorProceso, procesoKey);
incrementar(omitidasPorPrograma, programaKey);
incrementar(omitidasPorModalidad, modalidadKey);
incrementar(omitidasPorProcesoModalidad, procesoModalidadKey);
incrementar(omitidasPorCausa, "Modalidad no encontrada");
continue;
}
Modalidad modalidad = modalidadOpt.get();
boolean existe = inscripcionRepo.existsByPostulanteAndProceso(postulante, proceso);
if (existe) {
filasOmitidas++;
inscripcionDuplicada++;
incrementar(omitidasPorProceso, procesoKey);
incrementar(omitidasPorPrograma, programaKey);
incrementar(omitidasPorModalidad, modalidadKey);
incrementar(omitidasPorProcesoModalidad, procesoModalidadKey);
incrementar(omitidasPorCausa, "Inscripción duplicada");
continue;
}
Inscripcion inscripcion = new Inscripcion();
inscripcion.setFechaInscripcion(fechaInscripcion);
inscripcion.setPostulante(postulante);
inscripcion.setProceso(proceso);
inscripcion.setPrograma(programa);
inscripcion.setModalidad(modalidad);
inscripcionRepo.save(inscripcion);
inscripcionesGuardadas++;
String fechaInsStr = getCellStringValue(row.getCell(17));
LocalDateTime fechaInscripcion;
if (!fechaInsStr.isEmpty()) {
try {
fechaInscripcion = LocalDateTime.parse(fechaInsStr, fechaHoraFormatter);
} catch (Exception e) {
try { fechaInscripcion = LocalDate.parse(fechaInsStr).atStartOfDay(); }
catch (Exception ex) { fechaInscripcion = LocalDateTime.now(); }
errores++;
incrementar(omitidasPorCausa, "Error interno");
System.out.println("Fila " + (row.getRowNum() + 1) + ": ERROR -> " + e.getMessage());
}
} else {
fechaInscripcion = LocalDateTime.now();
}
}
String procesoStr = getCellStringValue(row.getCell(18));
if (procesoStr.isEmpty()) {
System.out.println("Fila " + row.getRowNum() + ": Proceso vacío, se omite");
continue;
}
Long procesoId;
try { procesoId = Long.parseLong(procesoStr); }
catch (NumberFormatException e) {
System.out.println("Fila " + row.getRowNum() + ": Proceso inválido -> " + procesoStr);
continue;
}
Optional<Proceso> procesoOpt = procesoRepo.findById(procesoId);
if (procesoOpt.isEmpty()) {
System.out.println("Fila " + row.getRowNum() + ": Proceso no encontrado -> " + procesoId);
continue;
}
Proceso proceso = procesoOpt.get();
StringBuilder sb = new StringBuilder();
sb.append("Importación finalizada.\n");
sb.append("Total filas: ").append(totalFilas).append("\n");
sb.append("Postulantes creados: ").append(postulantesCreados).append("\n");
sb.append("Inscripciones guardadas: ").append(inscripcionesGuardadas).append("\n");
sb.append("Filas omitidas: ").append(filasOmitidas).append("\n");
sb.append("Errores: ").append(errores).append("\n\n");
String programaStr = getCellStringValue(row.getCell(19));
Optional<ProgramaEstudio> programaOpt = programaRepo.findById(Long.parseLong(programaStr));
if (programaOpt.isEmpty()) {
System.out.println("Fila " + row.getRowNum() + ": Programa no encontrado -> " + programaStr);
continue;
}
ProgramaEstudio programa = programaOpt.get();
sb.append("DETALLE POR CAUSA:\n");
sb.append("- DNI vacío: ").append(dniVacio).append("\n");
sb.append("- Proceso vacío: ").append(procesoVacio).append("\n");
sb.append("- Proceso inválido: ").append(procesoInvalido).append("\n");
sb.append("- Proceso no encontrado: ").append(procesoNoEncontrado).append("\n");
sb.append("- Programa inválido: ").append(programaInvalido).append("\n");
sb.append("- Programa no encontrado: ").append(programaNoEncontrado).append("\n");
sb.append("- Modalidad inválida: ").append(modalidadInvalida).append("\n");
sb.append("- Modalidad no encontrada: ").append(modalidadNoEncontrada).append("\n");
sb.append("- Inscripción duplicada: ").append(inscripcionDuplicada).append("\n\n");
String modalidadStr = getCellStringValue(row.getCell(20));
Optional<Modalidad> modalidadOpt = modalidadRepo.findById(Long.parseLong(modalidadStr));
if (modalidadOpt.isEmpty()) {
System.out.println("Fila " + row.getRowNum() + ": Modalidad no encontrada -> " + modalidadStr);
continue;
}
Modalidad modalidad = modalidadOpt.get();
sb.append("OMITIDAS POR PROCESO:\n");
for (Map.Entry<String, Integer> entry : omitidasPorProceso.entrySet()) {
sb.append("- ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
}
boolean existe = inscripcionRepo.existsByPostulanteAndProceso(postulante, proceso);
if (existe) {
System.out.println("Fila " + row.getRowNum() + ": La inscripción ya existe para el postulante " + dni);
continue;
}
sb.append("\nOMITIDAS POR PROGRAMA:\n");
for (Map.Entry<String, Integer> entry : omitidasPorPrograma.entrySet()) {
sb.append("- ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
}
try {
Inscripcion inscripcion = new Inscripcion();
inscripcion.setFechaInscripcion(fechaInscripcion);
inscripcion.setPostulante(postulante);
inscripcion.setProceso(proceso);
inscripcion.setPrograma(programa);
inscripcion.setModalidad(modalidad);
inscripcionRepo.save(inscripcion);
System.out.println("Fila " + row.getRowNum() + ": Inscripción guardada -> " + dni);
} catch (Exception e) {
System.out.println("Fila " + row.getRowNum() + ": Error guardando inscripción -> " + e.getMessage());
}
sb.append("\nOMITIDAS POR MODALIDAD:\n");
for (Map.Entry<String, Integer> entry : omitidasPorModalidad.entrySet()) {
sb.append("- ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
}
sb.append("\nOMITIDAS POR PROCESO Y MODALIDAD:\n");
for (Map.Entry<String, Integer> entry : omitidasPorProcesoModalidad.entrySet()) {
sb.append("- ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
}
sb.append("\nRESUMEN DE CAUSAS AGRUPADAS:\n");
for (Map.Entry<String, Integer> entry : omitidasPorCausa.entrySet()) {
sb.append("- ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
}
workbook.close();
is.close();
return sb.toString();
}
}

@ -16,10 +16,10 @@ public class ResultadoService {
this.resultadoRepo = resultadoRepo;
}
// Buscar resultado por DNI y proceso
public Optional<ResultadoExamen> obtenerResultado(String dni, Long procesoId) {
// Buscar resultado por DNI, proceso y día
public Optional<ResultadoExamen> obtenerResultado(String dni, Long procesoId, Integer dia) {
return resultadoRepo.findByDniAndProceso(dni, procesoId);
return resultadoRepo.findByDniAndProcesoAndDia(dni, procesoId, dia);
}
// Ranking por proceso

Loading…
Cancel
Save