766 lines
28 KiB
TypeScript
766 lines
28 KiB
TypeScript
import {
|
|
Injectable,
|
|
InternalServerErrorException,
|
|
NotFoundException,
|
|
UnprocessableEntityException,
|
|
} from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { Repository, DataSource, In, MoreThan } from 'typeorm';
|
|
import {
|
|
CreateCuestionarioDto,
|
|
CreateCuestionarioEventoDto,
|
|
} from './dto/create-cuestionario.dto';
|
|
import { UpdateCuestionarioDto } from './dto/update-cuestionario.dto';
|
|
import { Cuestionario } from './entities/cuestionario.entity';
|
|
import { Seccion } from '../seccion/entities/seccion.entity';
|
|
import { CuestionarioSeccion } from '../cuestionario_seccion/entities/cuestionario_seccion.entity';
|
|
import { Pregunta, TiposValidacion } from '../pregunta/entities/pregunta.entity';
|
|
import { SeccionPregunta } from '../seccion_pregunta/entities/seccion_pregunta.entity';
|
|
import { Opcion } from '../opcion/entities/opcion.entity';
|
|
import { PreguntaOpcion } from '../pregunta_opcion/entities/pregunta_opcion.entity';
|
|
import {
|
|
TipoPregunta,
|
|
TipoPreguntaEnum,
|
|
} from '../tipo_pregunta/entities/tipo_pregunta.entity';
|
|
import { Evento } from '../evento/entities/evento.entity';
|
|
import { EventoService } from '../evento/evento.service';
|
|
import { CreateEventoWithCuestionarioDto } from './dto/create-evento-cuestionario.dto';
|
|
import * as ExcelJS from 'exceljs';
|
|
import { PLANTILLAS } from './plantillas';
|
|
|
|
@Injectable()
|
|
export class CuestionarioService {
|
|
constructor(
|
|
@InjectRepository(Cuestionario)
|
|
private cuestionarioRepository: Repository<Cuestionario>,
|
|
@InjectRepository(Seccion)
|
|
private seccionRepository: Repository<Seccion>,
|
|
@InjectRepository(CuestionarioSeccion)
|
|
private cuestionarioSeccionRepository: Repository<CuestionarioSeccion>,
|
|
@InjectRepository(Pregunta)
|
|
private preguntaRepository: Repository<Pregunta>,
|
|
@InjectRepository(SeccionPregunta)
|
|
private seccionPreguntaRepository: Repository<SeccionPregunta>,
|
|
@InjectRepository(Opcion)
|
|
private opcionRepository: Repository<Opcion>,
|
|
@InjectRepository(PreguntaOpcion)
|
|
private preguntaOpcionRepository: Repository<PreguntaOpcion>,
|
|
@InjectRepository(TipoPregunta)
|
|
private tipoPreguntaRepository: Repository<TipoPregunta>,
|
|
@InjectRepository(Evento)
|
|
private eventoRepository: Repository<Evento>,
|
|
private dataSource: DataSource,
|
|
private eventoService: EventoService,
|
|
) {}
|
|
|
|
|
|
|
|
private mapTipoPregunta(tipo: string): TipoPreguntaEnum {
|
|
switch (tipo.toLowerCase()) {
|
|
case 'cerrada': return TipoPreguntaEnum.Cerrada;
|
|
case 'multiple': return TipoPreguntaEnum.Multiple;
|
|
case 'abierta (parrafo)': return TipoPreguntaEnum.AbiertaParrafo;
|
|
case 'abierta (respuesta corta)': return TipoPreguntaEnum.AbiertaRespuestaCorta;
|
|
default: throw new Error(`Tipo de pregunta inválido: ${tipo}`);
|
|
}
|
|
}
|
|
|
|
// Mapear validación desde string a enum
|
|
private mapValidacion(validacion?: string): TiposValidacion | undefined {
|
|
if (!validacion) return undefined;
|
|
|
|
switch (validacion.trim().toLowerCase()) {
|
|
case 'correo': return TiposValidacion.CORREO;
|
|
case 'correo_institucional': return TiposValidacion.CORREO_INSTITUCIONAL;
|
|
case 'telefono': return TiposValidacion.TELEFONO;
|
|
case 'nombre': return TiposValidacion.NOMBRE;
|
|
case 'apellidos': return TiposValidacion.APELLIDOS;
|
|
case 'entero': return TiposValidacion.ENTERO;
|
|
case 'decimal': return TiposValidacion.DECIMAL;
|
|
case 'comunidad_alumno': return TiposValidacion.COMUNIDAD_ALUMNO;
|
|
case 'cuenta_alumno': return TiposValidacion.CUENTA_ALUMNO;
|
|
case 'comunidad_trabajador': return TiposValidacion.COMUNIDAD_TRABAJADOR;
|
|
case 'cuenta_trabajador': return TiposValidacion.CUENTA_TRABAJADOR;
|
|
case 'genero': return TiposValidacion.GENERO;
|
|
case 'carrera': return TiposValidacion.CARRERA;
|
|
case 'institucion': return TiposValidacion.INSTITUCION;
|
|
case 'rfc': return TiposValidacion.RFC;
|
|
default: return undefined;
|
|
}
|
|
}
|
|
|
|
|
|
async cargarEventosDesdeExcel(filePath: string) {
|
|
const PLANTILLA_MAP: Record<number, string> = {
|
|
1: 'registro-comunidad-alumnos',
|
|
2: 'registro-comunidad-trabajadores',
|
|
3: 'registro-general',
|
|
};
|
|
|
|
const workbook = new ExcelJS.Workbook();
|
|
await workbook.xlsx.readFile(filePath);
|
|
|
|
const eventosSheet = workbook.getWorksheet('Eventos');
|
|
const cuestionariosSheet = workbook.getWorksheet('Cuestionarios');
|
|
const seccionesSheet = workbook.getWorksheet('Secciones');
|
|
const preguntasSheet = workbook.getWorksheet('Preguntas');
|
|
const opcionesSheet = workbook.getWorksheet('Opciones');
|
|
|
|
if (!eventosSheet || !cuestionariosSheet || !seccionesSheet || !preguntasSheet || !opcionesSheet) {
|
|
throw new UnprocessableEntityException('El archivo Excel debe contener las hojas: Eventos, Cuestionarios, Secciones, Preguntas y Opciones.');
|
|
}
|
|
|
|
for (let i = 2; i <= eventosSheet.rowCount; i++) {
|
|
const row = eventosSheet.getRow(i);
|
|
|
|
// Construir objeto evento
|
|
const eventoDto = {
|
|
tipo_evento: row.getCell(2).value as string,
|
|
nombre_evento: row.getCell(3).value as string,
|
|
descripcion_evento: row.getCell(4).value as string,
|
|
fecha_inicio: new Date(row.getCell(5).value as string),
|
|
fecha_fin: new Date(row.getCell(6).value as string),
|
|
banner: row.getCell(7).value as string, // nombre del archivo subido previamente
|
|
};
|
|
|
|
// Filtrar cuestionarios de este evento
|
|
const cuestionarios = cuestionariosSheet.getRows(2, cuestionariosSheet.rowCount - 1)
|
|
?.filter(qRow => qRow.getCell(2).value === row.getCell(1).value) || [];
|
|
|
|
for (const qRow of cuestionarios) {
|
|
|
|
const plantilla = qRow.getCell(9).value ? Number(row.getCell(8).value) : null;
|
|
|
|
|
|
if (plantilla && PLANTILLA_MAP[plantilla]) {
|
|
const fecha_inicio = new Date(qRow.getCell(4).value as string);
|
|
const fecha_fin = new Date(qRow.getCell(5).value as string);
|
|
const id_tipo_evento= Number(qRow.getCell(8).value);
|
|
const plantillaId = PLANTILLA_MAP[plantilla];
|
|
const base = PLANTILLAS.find(p => p.id === plantillaId);
|
|
if (!base) throw new Error(`Plantilla ${plantillaId} no encontrada`);
|
|
return {
|
|
...base.datos,
|
|
fecha_inicio,
|
|
fecha_fin,
|
|
id_tipo_evento,
|
|
|
|
|
|
};
|
|
}
|
|
|
|
|
|
// Filtrar secciones de este cuestionario
|
|
const secciones = seccionesSheet.getRows(2, seccionesSheet.rowCount - 1)
|
|
?.filter(sRow => sRow.getCell(2).value === qRow.getCell(1).value)
|
|
.map(sRow => {
|
|
// Filtrar preguntas de esta sección
|
|
const preguntas = preguntasSheet.getRows(2, preguntasSheet.rowCount - 1)
|
|
?.filter(pRow => pRow.getCell(2).value === sRow.getCell(1).value)
|
|
.map(pRow => {
|
|
const opciones = opcionesSheet.getRows(2, opcionesSheet.rowCount - 1)
|
|
?.filter(oRow => oRow.getCell(2).value === pRow.getCell(1).value)
|
|
.map(oRow => ({ valor: oRow.getCell(3).value as string })) || [];
|
|
|
|
return {
|
|
titulo: pRow.getCell(3).value as string,
|
|
tipo: this.mapTipoPregunta(pRow.getCell(4).value as string),
|
|
obligatoria: !!pRow.getCell(5).value,
|
|
validacion: this.mapValidacion(pRow.getCell(6).value as string),
|
|
opciones,
|
|
};
|
|
}) || [];
|
|
|
|
return {
|
|
titulo: sRow.getCell(3).value as string,
|
|
descripcion: sRow.getCell(4).value as string,
|
|
preguntas,
|
|
};
|
|
}) || [];
|
|
|
|
// Construir DTO completo
|
|
const createDto: CreateEventoWithCuestionarioDto = {
|
|
evento: eventoDto,
|
|
cuestionario: {
|
|
nombre_form: qRow.getCell(3).value as string,
|
|
descripcion: qRow.getCell(4).value as string,
|
|
fecha_inicio: new Date(qRow.getCell(5).value as string),
|
|
fecha_fin: new Date(qRow.getCell(6).value as string),
|
|
id_tipo_cuestionario: Number(qRow.getCell(7).value),
|
|
id_tipo_evento: Number(qRow.getCell(8).value), // obligatorio
|
|
secciones,
|
|
},
|
|
};
|
|
|
|
// Llamada a tu servicio para guardar en DB
|
|
await this.createCuestionarioEvento(createDto);
|
|
}
|
|
}
|
|
|
|
return { message: 'Carga masiva completada' };
|
|
}
|
|
|
|
async create(createCuestionarioDto: CreateCuestionarioDto) {
|
|
// Verificar que el evento exista
|
|
await this.eventoService.getEventoOrFail(createCuestionarioDto.id_evento);
|
|
|
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
await queryRunner.connect();
|
|
await queryRunner.startTransaction();
|
|
|
|
try {
|
|
// 1. Crear cuestionario
|
|
const cuestionario = new Cuestionario();
|
|
cuestionario.nombre_form = createCuestionarioDto.nombre_form;
|
|
cuestionario.descripcion = createCuestionarioDto.descripcion;
|
|
cuestionario.fecha_inicio = new Date(createCuestionarioDto.fecha_inicio);
|
|
cuestionario.fecha_fin = new Date(createCuestionarioDto.fecha_fin);
|
|
cuestionario.id_tipo_cuestionario =
|
|
createCuestionarioDto.id_tipo_cuestionario;
|
|
cuestionario.id_tipo_evento = createCuestionarioDto.id_tipo_evento;
|
|
cuestionario.contador_secciones =
|
|
createCuestionarioDto.secciones?.length || 0;
|
|
cuestionario.editable = true;
|
|
cuestionario.id_evento = createCuestionarioDto.id_evento;
|
|
cuestionario.cupo_maximo = createCuestionarioDto.cupo_maximo;
|
|
|
|
const savedCuestionario = await queryRunner.manager.save(cuestionario);
|
|
|
|
// 2. Crear secciones y vincularlas
|
|
if (
|
|
createCuestionarioDto.secciones &&
|
|
createCuestionarioDto.secciones?.length > 0
|
|
) {
|
|
for (let i = 0; i < createCuestionarioDto.secciones.length; i++) {
|
|
const seccionDto = createCuestionarioDto.secciones[i];
|
|
|
|
const seccion = new Seccion();
|
|
seccion.titulo = seccionDto.titulo;
|
|
seccion.descripcion = seccionDto.descripcion;
|
|
seccion.contador_pregunta = seccionDto.preguntas?.length || 0;
|
|
|
|
const savedSeccion = await queryRunner.manager.save(seccion);
|
|
|
|
const cuestionarioSeccion = new CuestionarioSeccion();
|
|
cuestionarioSeccion.id_cuestionario =
|
|
savedCuestionario.id_cuestionario;
|
|
cuestionarioSeccion.id_seccion = savedSeccion.id_seccion;
|
|
cuestionarioSeccion.posicion = i + 1;
|
|
|
|
await queryRunner.manager.save(cuestionarioSeccion);
|
|
|
|
// 3. Preguntas por sección
|
|
if (seccionDto.preguntas && seccionDto.preguntas?.length > 0) {
|
|
for (let j = 0; j < seccionDto.preguntas.length; j++) {
|
|
const preguntaDto = seccionDto.preguntas[j];
|
|
|
|
let idTipoPregunta = 7;
|
|
|
|
switch (preguntaDto.tipo) {
|
|
case TipoPreguntaEnum.AbiertaParrafo:
|
|
idTipoPregunta = 1;
|
|
break;
|
|
case TipoPreguntaEnum.AbiertaRespuestaCorta:
|
|
idTipoPregunta = 2;
|
|
break;
|
|
case TipoPreguntaEnum.Cerrada:
|
|
idTipoPregunta = 3;
|
|
break;
|
|
case TipoPreguntaEnum.Multiple:
|
|
idTipoPregunta = 4;
|
|
break;
|
|
default:
|
|
console.warn(
|
|
`Tipo de pregunta no reconocido: ${preguntaDto.tipo}. Se asignará como desconocido.`,
|
|
);
|
|
idTipoPregunta = 7;
|
|
break;
|
|
}
|
|
|
|
const pregunta = new Pregunta();
|
|
pregunta.pregunta = preguntaDto.titulo;
|
|
pregunta.obligatoria = preguntaDto.obligatoria || false;
|
|
pregunta.id_tipo_pregunta = idTipoPregunta;
|
|
pregunta.contador_opcion = preguntaDto.opciones?.length || 0;
|
|
pregunta.validacion = preguntaDto.validacion || undefined;
|
|
|
|
const savedPregunta = await queryRunner.manager.save(pregunta);
|
|
|
|
const seccionPregunta = new SeccionPregunta();
|
|
seccionPregunta.id_seccion = savedSeccion.id_seccion;
|
|
seccionPregunta.id_pregunta = savedPregunta.id_pregunta;
|
|
seccionPregunta.posicion = j + 1;
|
|
|
|
await queryRunner.manager.save(seccionPregunta);
|
|
|
|
// 4. Crear opciones si las hay
|
|
if (preguntaDto.opciones && preguntaDto.opciones?.length > 0) {
|
|
for (let k = 0; k < preguntaDto.opciones.length; k++) {
|
|
const opcionDto = preguntaDto.opciones[k];
|
|
|
|
const opcion = new Opcion();
|
|
opcion.opcion = opcionDto.valor;
|
|
|
|
const savedOpcion = await queryRunner.manager.save(opcion);
|
|
|
|
const preguntaOpcion = new PreguntaOpcion();
|
|
preguntaOpcion.id_pregunta = savedPregunta.id_pregunta;
|
|
preguntaOpcion.opcion = savedOpcion;
|
|
preguntaOpcion.posicion = k + 1;
|
|
|
|
await queryRunner.manager.save(preguntaOpcion);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
await queryRunner.commitTransaction();
|
|
|
|
return {
|
|
success: true,
|
|
message: 'Cuestionario creado exitosamente',
|
|
id_cuestionario: savedCuestionario.id_cuestionario,
|
|
id_evento: savedCuestionario.id_evento,
|
|
};
|
|
} catch (error) {
|
|
await queryRunner.rollbackTransaction();
|
|
throw new InternalServerErrorException(
|
|
'Error al crear el cuestionario y evento',
|
|
error.message,
|
|
);
|
|
} finally {
|
|
await queryRunner.release();
|
|
}
|
|
}
|
|
|
|
async findAllRecientes() {
|
|
console.log('Buscando cuestionarios recientes');
|
|
const now = new Date();
|
|
const pastDate = new Date();
|
|
pastDate.setDate(now.getDate() - 30);
|
|
|
|
return this.cuestionarioRepository.find({
|
|
where: {
|
|
fecha_inicio: MoreThan(pastDate),
|
|
},
|
|
order: { fecha_fin: 'DESC' },
|
|
});
|
|
}
|
|
|
|
async createCuestionarioEvento(
|
|
createCuestionarioDto: CreateEventoWithCuestionarioDto,
|
|
) {
|
|
const { evento, cuestionario } = createCuestionarioDto;
|
|
|
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
await queryRunner.connect();
|
|
await queryRunner.startTransaction();
|
|
|
|
try {
|
|
// Buscar si ya existe un evento con el nombre proporcionado
|
|
let eventoExistente = await this.eventoRepository.findOne({
|
|
where: { nombre_evento: evento.nombre_evento },
|
|
});
|
|
|
|
// Si el evento no existe, crearlo
|
|
if (!eventoExistente) {
|
|
eventoExistente = queryRunner.manager.create(Evento, evento);
|
|
await queryRunner.manager.save(eventoExistente);
|
|
console.log(`Creando nuevo evento: ${evento.nombre_evento}`);
|
|
}
|
|
|
|
// Crear el cuestionario asociado al evento
|
|
const cuestionarioEntity = queryRunner.manager.create(Cuestionario, {
|
|
nombre_form: cuestionario.nombre_form,
|
|
descripcion: cuestionario.descripcion,
|
|
fecha_inicio: new Date(cuestionario.fecha_inicio),
|
|
fecha_fin: new Date(cuestionario.fecha_fin),
|
|
id_tipo_cuestionario: cuestionario.id_tipo_cuestionario,
|
|
id_tipo_evento: cuestionario.id_tipo_evento,
|
|
contador_secciones: cuestionario.secciones?.length || 0,
|
|
editable: true,
|
|
id_evento: eventoExistente.id_evento, // Asociar el evento creado
|
|
cupo_maximo: cuestionario.cupo_maximo,
|
|
});
|
|
const savedCuestionario =
|
|
await queryRunner.manager.save(cuestionarioEntity);
|
|
|
|
console.log(
|
|
`Cuestionario creado: ${savedCuestionario.nombre_form}, ID: ${savedCuestionario.id_cuestionario}`,
|
|
);
|
|
|
|
// Crear las secciones del cuestionario
|
|
if (
|
|
createCuestionarioDto.cuestionario.secciones &&
|
|
createCuestionarioDto.cuestionario.secciones.length > 0
|
|
) {
|
|
for (
|
|
let i = 0;
|
|
i < createCuestionarioDto.cuestionario.secciones.length;
|
|
i++
|
|
) {
|
|
const seccionDto = createCuestionarioDto.cuestionario.secciones[i];
|
|
|
|
// Crear sección
|
|
const seccion = new Seccion();
|
|
seccion.titulo = seccionDto.titulo;
|
|
seccion.descripcion = seccionDto.descripcion;
|
|
seccion.contador_pregunta = seccionDto.preguntas?.length || 0;
|
|
|
|
const savedSeccion = await queryRunner.manager.save(seccion);
|
|
|
|
// Vincular sección con cuestionario
|
|
const cuestionarioSeccion = new CuestionarioSeccion();
|
|
cuestionarioSeccion.id_cuestionario =
|
|
savedCuestionario.id_cuestionario;
|
|
cuestionarioSeccion.id_seccion = savedSeccion.id_seccion;
|
|
cuestionarioSeccion.posicion = i + 1;
|
|
|
|
await queryRunner.manager.save(cuestionarioSeccion);
|
|
|
|
// 3. Crear preguntas y vincularlas a la sección
|
|
if (seccionDto.preguntas && seccionDto.preguntas.length > 0) {
|
|
for (let j = 0; j < seccionDto.preguntas.length; j++) {
|
|
const preguntaDto = seccionDto.preguntas[j];
|
|
|
|
let idTipoPregunta = 7;
|
|
|
|
switch (preguntaDto.tipo) {
|
|
case TipoPreguntaEnum.AbiertaParrafo:
|
|
idTipoPregunta = 1;
|
|
break;
|
|
case TipoPreguntaEnum.AbiertaRespuestaCorta:
|
|
idTipoPregunta = 2;
|
|
break;
|
|
case TipoPreguntaEnum.Cerrada:
|
|
idTipoPregunta = 3;
|
|
break;
|
|
case TipoPreguntaEnum.Multiple:
|
|
idTipoPregunta = 4;
|
|
break;
|
|
default:
|
|
console.warn(
|
|
`Tipo de pregunta no reconocido: ${preguntaDto.tipo}. Se asignará como desconocido.`,
|
|
);
|
|
idTipoPregunta = 7;
|
|
break;
|
|
}
|
|
|
|
const tipoPregunta = {
|
|
id_tipo: idTipoPregunta,
|
|
tipo_pregunta: preguntaDto.tipo,
|
|
};
|
|
|
|
// Crear pregunta
|
|
const pregunta = new Pregunta();
|
|
pregunta.pregunta = preguntaDto.titulo;
|
|
pregunta.obligatoria = preguntaDto.obligatoria || false;
|
|
pregunta.id_tipo_pregunta = tipoPregunta.id_tipo;
|
|
pregunta.contador_opcion = preguntaDto.opciones?.length || 0;
|
|
pregunta.validacion = preguntaDto.validacion || undefined;
|
|
|
|
const savedPregunta = await queryRunner.manager.save(pregunta);
|
|
|
|
// Vincular pregunta con sección
|
|
const seccionPregunta = new SeccionPregunta();
|
|
seccionPregunta.id_seccion = savedSeccion.id_seccion;
|
|
seccionPregunta.id_pregunta = savedPregunta.id_pregunta;
|
|
seccionPregunta.posicion = j + 1;
|
|
|
|
await queryRunner.manager.save(seccionPregunta);
|
|
|
|
// 4. Crear opciones para preguntas de tipo multiple/radio
|
|
if (preguntaDto.opciones && preguntaDto.opciones.length > 0) {
|
|
for (let k = 0; k < preguntaDto.opciones.length; k++) {
|
|
const opcionDto = preguntaDto.opciones[k];
|
|
|
|
// Crear opción
|
|
const opcion = new Opcion();
|
|
opcion.opcion = opcionDto.valor;
|
|
|
|
const savedOpcion = await queryRunner.manager.save(opcion);
|
|
|
|
// Vincular opción con pregunta
|
|
const preguntaOpcion = new PreguntaOpcion();
|
|
preguntaOpcion.id_pregunta = savedPregunta.id_pregunta;
|
|
// Utilizamos la opción como objeto, no como ID
|
|
preguntaOpcion.opcion = savedOpcion;
|
|
preguntaOpcion.posicion = k + 1;
|
|
|
|
await queryRunner.manager.save(preguntaOpcion);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
await queryRunner.commitTransaction();
|
|
return {
|
|
success: true,
|
|
message: 'Cuestionario y evento creados exitosamente',
|
|
id_cuestionario: savedCuestionario.id_cuestionario,
|
|
id_evento: eventoExistente.id_evento,
|
|
};
|
|
} catch (error) {
|
|
await queryRunner.rollbackTransaction();
|
|
throw error;
|
|
} finally {
|
|
await queryRunner.release();
|
|
}
|
|
}
|
|
|
|
async asociarBanner(id: number, banner: string) {
|
|
const cuestionario = await this.cuestionarioRepository.findOne({
|
|
where: { id_cuestionario: id },
|
|
});
|
|
|
|
if (!cuestionario) {
|
|
throw new NotFoundException(`Cuestionario con ID ${id} no encontrado`);
|
|
}
|
|
|
|
// Actualizar el banner del cuestionario
|
|
cuestionario.banner = banner;
|
|
return this.cuestionarioRepository.save(cuestionario);
|
|
}
|
|
|
|
async getCuestionarioOrFail(id_cuestionario: number): Promise<Cuestionario> {
|
|
const cuestionario = await this.cuestionarioRepository.findOne({
|
|
where: { id_cuestionario: id_cuestionario },
|
|
relations: ['evento'],
|
|
});
|
|
|
|
if (!cuestionario) {
|
|
throw new NotFoundException(
|
|
`Cuestionario con ID ${id_cuestionario} no encontrado`,
|
|
);
|
|
}
|
|
|
|
return cuestionario;
|
|
}
|
|
|
|
findAll() {
|
|
return this.cuestionarioRepository.find();
|
|
}
|
|
|
|
async findOne(id: number) {
|
|
const cuestionario = await this.cuestionarioRepository.findOne({
|
|
where: { id_cuestionario: id },
|
|
});
|
|
|
|
if (!cuestionario) {
|
|
throw new NotFoundException(`Cuestionario con ID ${id} no encontrado`);
|
|
}
|
|
|
|
return cuestionario;
|
|
}
|
|
|
|
async findCompleto(id: number) {
|
|
// Obtener el cuestionario básico
|
|
const cuestionario = await this.cuestionarioRepository.findOne({
|
|
where: { id_cuestionario: id },
|
|
relations: ['evento'], // Incluir la relación con el evento
|
|
});
|
|
|
|
if (!cuestionario) {
|
|
throw new NotFoundException(`Cuestionario con ID ${id} no encontrado`);
|
|
}
|
|
|
|
// Para fines de demostración, usar valores hardcodeados
|
|
// para el tipo de cuestionario según get_formulario_feria.ts
|
|
const tipoCuestionario = {
|
|
id_tipo_cuestionario: 1,
|
|
tipo_cuestionario: 'Encuesta',
|
|
};
|
|
|
|
// Mapeador de ID de tipo de pregunta a nombres conocidos
|
|
const tiposPreguntaMap = {
|
|
1: { id_tipo: 1, tipo_pregunta: TipoPreguntaEnum.AbiertaParrafo },
|
|
2: { id_tipo: 2, tipo_pregunta: TipoPreguntaEnum.AbiertaRespuestaCorta },
|
|
3: { id_tipo: 3, tipo_pregunta: TipoPreguntaEnum.Cerrada },
|
|
4: { id_tipo: 4, tipo_pregunta: TipoPreguntaEnum.Multiple },
|
|
};
|
|
|
|
// Obtener las relaciones cuestionario-seccion
|
|
const cuestionarioSecciones = await this.cuestionarioSeccionRepository.find(
|
|
{
|
|
where: { id_cuestionario: id },
|
|
order: { posicion: 'ASC' },
|
|
},
|
|
);
|
|
|
|
// Obtener todas las secciones
|
|
const seccionIds = cuestionarioSecciones.map((cs) => cs.id_seccion);
|
|
const secciones = await this.seccionRepository.find({
|
|
where: { id_seccion: In(seccionIds) },
|
|
});
|
|
|
|
const seccionesFormateadas = await Promise.all(
|
|
cuestionarioSecciones.map(async (cs) => {
|
|
const seccion = secciones.find((s) => s.id_seccion === cs.id_seccion);
|
|
|
|
if (!seccion) return null;
|
|
|
|
// Obtener relaciones sección-pregunta
|
|
const seccionPreguntas = await this.seccionPreguntaRepository.find({
|
|
where: { id_seccion: seccion.id_seccion },
|
|
order: { posicion: 'ASC' },
|
|
});
|
|
|
|
// Obtener preguntas
|
|
const preguntaIds = seccionPreguntas.map((sp) => sp.id_pregunta);
|
|
const preguntas = await this.preguntaRepository.find({
|
|
where: { id_pregunta: In(preguntaIds) },
|
|
});
|
|
|
|
// Formatear cada pregunta según el formato requerido
|
|
const preguntasFormateadas = await Promise.all(
|
|
seccionPreguntas.map(async (sp) => {
|
|
const pregunta = preguntas.find(
|
|
(p) => p.id_pregunta === sp.id_pregunta,
|
|
);
|
|
|
|
if (!pregunta) return null;
|
|
|
|
// Usar el mapeo de tipos de pregunta definido arriba
|
|
const tipoPregunta = tiposPreguntaMap[
|
|
pregunta.id_tipo_pregunta
|
|
] || {
|
|
id_tipo: pregunta.id_tipo_pregunta,
|
|
tipo_pregunta: 'Desconocido',
|
|
};
|
|
|
|
// Obtener opciones para preguntas de tipo multiple/radio
|
|
const preguntaOpciones = await this.preguntaOpcionRepository.find({
|
|
where: { id_pregunta: pregunta.id_pregunta },
|
|
relations: ['opcion'],
|
|
order: { posicion: 'ASC' },
|
|
});
|
|
|
|
// Formatear las opciones según el formato requerido
|
|
const opcionesFormateadas = preguntaOpciones.map((po) => ({
|
|
id_pregunta_opcion: po.id_pregunta_opcion,
|
|
posicion: po.posicion,
|
|
id_opcion: po.opcion.id_opcion,
|
|
opcion: {
|
|
id_opcion: po.opcion.id_opcion,
|
|
opcion: po.opcion.opcion,
|
|
},
|
|
}));
|
|
|
|
// Construir objeto de pregunta según el formato requerido
|
|
return {
|
|
id_seccion_pregunta: sp.id_seccion_pregunta,
|
|
posicion: sp.posicion,
|
|
pregunta: {
|
|
id_pregunta: pregunta.id_pregunta,
|
|
pregunta: pregunta.pregunta,
|
|
contador_opcion: pregunta.contador_opcion,
|
|
obligatoria: pregunta.obligatoria,
|
|
id_tipo_pregunta: pregunta.id_tipo_pregunta,
|
|
validacion: pregunta.validacion,
|
|
tipo_pregunta: {
|
|
id_tipo: tipoPregunta.id_tipo,
|
|
tipo_pregunta: tipoPregunta.tipo_pregunta,
|
|
},
|
|
opciones: opcionesFormateadas,
|
|
},
|
|
};
|
|
}),
|
|
).then((results) => results.filter((p) => p !== null));
|
|
|
|
// Construir objeto de sección según el formato requerido
|
|
return {
|
|
id_cuestionario_seccion: cs.id_cuestionario_seccion,
|
|
posicion: cs.posicion,
|
|
seccion: {
|
|
id_seccion: seccion.id_seccion,
|
|
contador_pregunta: seccion.contador_pregunta,
|
|
descripcion: seccion.descripcion,
|
|
titulo: seccion.titulo,
|
|
},
|
|
preguntas: preguntasFormateadas,
|
|
};
|
|
}),
|
|
).then((results) => results.filter((s) => s !== null));
|
|
|
|
// Construir objeto final respetando el formato de get_formulario_feria.ts
|
|
return {
|
|
tipo_cuestionario: {
|
|
id_tipo_cuestionario: tipoCuestionario.id_tipo_cuestionario,
|
|
tipo_cuestionario: tipoCuestionario.tipo_cuestionario,
|
|
},
|
|
evento: cuestionario.evento
|
|
? {
|
|
id_evento: cuestionario.evento.id_evento,
|
|
nombre_evento: cuestionario.evento.nombre_evento,
|
|
descripcion_evento: cuestionario.evento.descripcion_evento,
|
|
banner: cuestionario.evento.banner,
|
|
tipo_evento: cuestionario.evento.tipo_evento,
|
|
fecha_inicio: cuestionario.evento.fecha_inicio
|
|
? cuestionario.evento.fecha_inicio.toISOString()
|
|
: null,
|
|
fecha_fin: cuestionario.evento.fecha_fin
|
|
? cuestionario.evento.fecha_fin.toISOString()
|
|
: null,
|
|
}
|
|
: null,
|
|
cuestionario: {
|
|
id_cuestionario: cuestionario.id_cuestionario,
|
|
nombre_form: cuestionario.nombre_form,
|
|
contador_secciones: cuestionario.contador_secciones,
|
|
descripcion: cuestionario.descripcion,
|
|
editable: cuestionario.editable,
|
|
fecha_inicio: cuestionario.fecha_inicio
|
|
? cuestionario.fecha_inicio.toISOString()
|
|
: null,
|
|
fecha_fin: cuestionario.fecha_fin
|
|
? cuestionario.fecha_fin.toISOString()
|
|
: null,
|
|
id_tipo_cuestionario: cuestionario.id_tipo_cuestionario,
|
|
id_evento: cuestionario.id_evento || null,
|
|
secciones: seccionesFormateadas,
|
|
},
|
|
};
|
|
}
|
|
|
|
async update(id: number, updateCuestionarioDto: UpdateCuestionarioDto) {
|
|
// Si el DTO incluye el campo "evento" (string), debemos procesarlo aparte
|
|
if (updateCuestionarioDto.evento) {
|
|
// Buscar o crear evento
|
|
let evento = await this.eventoRepository.findOne({
|
|
where: { nombre_evento: updateCuestionarioDto.evento },
|
|
});
|
|
|
|
if (!evento) {
|
|
// Crear el evento
|
|
const nuevoEvento = {
|
|
nombre_evento: updateCuestionarioDto.evento,
|
|
tipo_evento: 'Predeterminado',
|
|
fecha_inicio: new Date(),
|
|
fecha_fin: new Date(Date.now() + 86400000),
|
|
};
|
|
|
|
evento = await this.eventoRepository.save(nuevoEvento);
|
|
}
|
|
|
|
// Asignar el ID del evento al DTO de actualización
|
|
updateCuestionarioDto.id_evento = evento.id_evento;
|
|
|
|
// Eliminar la propiedad evento para evitar conflictos
|
|
delete updateCuestionarioDto.evento;
|
|
}
|
|
|
|
// Preparar los datos para la actualización
|
|
const updateData: any = { ...updateCuestionarioDto };
|
|
|
|
return this.cuestionarioRepository.update(id, updateData);
|
|
}
|
|
|
|
remove(id: number) {
|
|
return this.cuestionarioRepository.delete(id);
|
|
}
|
|
}
|