6 Commits

50 changed files with 1777 additions and 243 deletions
+185 -2
View File
@@ -14,8 +14,11 @@
"@nestjs/core": "^11.0.1", "@nestjs/core": "^11.0.1",
"@nestjs/mapped-types": "*", "@nestjs/mapped-types": "*",
"@nestjs/platform-express": "^11.0.1", "@nestjs/platform-express": "^11.0.1",
"@nestjs/swagger": "^11.1.0",
"@nestjs/typeorm": "^11.0.0", "@nestjs/typeorm": "^11.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1", "class-validator": "^0.14.1",
"mysql2": "^3.14.0",
"reflect-metadata": "^0.2.2", "reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
"typeorm": "^0.3.21" "typeorm": "^0.3.21"
@@ -1906,6 +1909,12 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/@microsoft/tsdoc": {
"version": "0.15.1",
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz",
"integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==",
"license": "MIT"
},
"node_modules/@napi-rs/nice": { "node_modules/@napi-rs/nice": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.0.1.tgz",
@@ -2495,6 +2504,39 @@
"tslib": "^2.1.0" "tslib": "^2.1.0"
} }
}, },
"node_modules/@nestjs/swagger": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-11.1.0.tgz",
"integrity": "sha512-+GQ+q1ASTBvGi0DYHukWi8NVVVLszedwLLqHdLRnJh8rjokt8YTDb7roImvT/YMmYgPvaWBv/4JYdZH4FueLPQ==",
"license": "MIT",
"dependencies": {
"@microsoft/tsdoc": "0.15.1",
"@nestjs/mapped-types": "2.1.0",
"js-yaml": "4.1.0",
"lodash": "4.17.21",
"path-to-regexp": "8.2.0",
"swagger-ui-dist": "5.20.1"
},
"peerDependencies": {
"@fastify/static": "^8.0.0",
"@nestjs/common": "^11.0.1",
"@nestjs/core": "^11.0.1",
"class-transformer": "*",
"class-validator": "*",
"reflect-metadata": "^0.1.12 || ^0.2.0"
},
"peerDependenciesMeta": {
"@fastify/static": {
"optional": true
},
"class-transformer": {
"optional": true
},
"class-validator": {
"optional": true
}
}
},
"node_modules/@nestjs/testing": { "node_modules/@nestjs/testing": {
"version": "11.0.12", "version": "11.0.12",
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.0.12.tgz", "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-11.0.12.tgz",
@@ -2613,6 +2655,13 @@
"url": "https://opencollective.com/unts" "url": "https://opencollective.com/unts"
} }
}, },
"node_modules/@scarf/scarf": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
"integrity": "sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==",
"hasInstallScript": true,
"license": "Apache-2.0"
},
"node_modules/@sec-ant/readable-stream": { "node_modules/@sec-ant/readable-stream": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
@@ -4115,7 +4164,6 @@
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"dev": true,
"license": "Python-2.0" "license": "Python-2.0"
}, },
"node_modules/array-timsort": { "node_modules/array-timsort": {
@@ -4146,6 +4194,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/aws-ssl-profiles": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
"integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
"license": "MIT",
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/b4a": { "node_modules/b4a": {
"version": "1.6.7", "version": "1.6.7",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
@@ -4746,6 +4803,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/class-transformer": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
"integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==",
"license": "MIT"
},
"node_modules/class-validator": { "node_modules/class-validator": {
"version": "0.14.1", "version": "0.14.1",
"resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz",
@@ -5248,6 +5311,15 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/denque": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": { "node_modules/depd": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -6499,6 +6571,15 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/generate-function": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
"license": "MIT",
"dependencies": {
"is-property": "^1.0.2"
}
},
"node_modules/gensync": { "node_modules/gensync": {
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@@ -7077,6 +7158,12 @@
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==",
"license": "MIT"
},
"node_modules/is-stream": { "node_modules/is-stream": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -7912,7 +7999,6 @@
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"argparse": "^2.0.1" "argparse": "^2.0.1"
@@ -8125,6 +8211,12 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/long": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz",
"integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==",
"license": "Apache-2.0"
},
"node_modules/lowercase-keys": { "node_modules/lowercase-keys": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
@@ -8148,6 +8240,21 @@
"yallist": "^3.0.2" "yallist": "^3.0.2"
} }
}, },
"node_modules/lru.min": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.2.tgz",
"integrity": "sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==",
"license": "MIT",
"engines": {
"bun": ">=1.0.0",
"deno": ">=1.30.0",
"node": ">=8.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wellwelwel"
}
},
"node_modules/magic-string": { "node_modules/magic-string": {
"version": "0.30.17", "version": "0.30.17",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
@@ -8464,6 +8571,59 @@
"node": "^18.17.0 || >=20.5.0" "node": "^18.17.0 || >=20.5.0"
} }
}, },
"node_modules/mysql2": {
"version": "3.14.0",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.14.0.tgz",
"integrity": "sha512-8eMhmG6gt/hRkU1G+8KlGOdQi2w+CgtNoD1ksXZq9gQfkfDsX4LHaBwTe1SY0Imx//t2iZA03DFnyYKPinxSRw==",
"license": "MIT",
"dependencies": {
"aws-ssl-profiles": "^1.1.1",
"denque": "^2.1.0",
"generate-function": "^2.3.1",
"iconv-lite": "^0.6.3",
"long": "^5.2.1",
"lru.min": "^1.0.0",
"named-placeholders": "^1.1.3",
"seq-queue": "^0.0.5",
"sqlstring": "^2.3.2"
},
"engines": {
"node": ">= 8.0"
}
},
"node_modules/mysql2/node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/named-placeholders": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
"integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==",
"license": "MIT",
"dependencies": {
"lru-cache": "^7.14.1"
},
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/named-placeholders/node_modules/lru-cache": {
"version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
"integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/natural-compare": { "node_modules/natural-compare": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@@ -9637,6 +9797,11 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/seq-queue": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz",
"integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="
},
"node_modules/serialize-javascript": { "node_modules/serialize-javascript": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
@@ -9883,6 +10048,15 @@
"node": ">=14" "node": ">=14"
} }
}, },
"node_modules/sqlstring": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz",
"integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/stack-utils": { "node_modules/stack-utils": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz",
@@ -10220,6 +10394,15 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/swagger-ui-dist": {
"version": "5.20.1",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.20.1.tgz",
"integrity": "sha512-qBPCis2w8nP4US7SvUxdJD3OwKcqiWeZmjN2VWhq2v+ESZEXOP/7n4DeiOiiZcGYTKMHAHUUrroHaTsjUWTEGw==",
"license": "Apache-2.0",
"dependencies": {
"@scarf/scarf": "=1.4.0"
}
},
"node_modules/symbol-observable": { "node_modules/symbol-observable": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
+3
View File
@@ -25,8 +25,11 @@
"@nestjs/core": "^11.0.1", "@nestjs/core": "^11.0.1",
"@nestjs/mapped-types": "*", "@nestjs/mapped-types": "*",
"@nestjs/platform-express": "^11.0.1", "@nestjs/platform-express": "^11.0.1",
"@nestjs/swagger": "^11.1.0",
"@nestjs/typeorm": "^11.0.0", "@nestjs/typeorm": "^11.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1", "class-validator": "^0.14.1",
"mysql2": "^3.14.0",
"reflect-metadata": "^0.2.2", "reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
"typeorm": "^0.3.21" "typeorm": "^0.3.21"
+10 -5
View File
@@ -1,4 +1,4 @@
import { Module } from '@nestjs/common'; import { forwardRef, Module } from '@nestjs/common';
import { AppController } from './app.controller'; import { AppController } from './app.controller';
import { AppService } from './app.service'; import { AppService } from './app.service';
import { CuestionarioModule } from './cuestionario/cuestionario.module'; import { CuestionarioModule } from './cuestionario/cuestionario.module';
@@ -15,6 +15,7 @@ import { CuestionarioRespondidoModule } from './cuestionario_respondido/cuestion
import { RespuestaParticipanteAbiertaModule } from './respuesta_participante_abierta/respuesta_participante_abierta.module'; import { RespuestaParticipanteAbiertaModule } from './respuesta_participante_abierta/respuesta_participante_abierta.module';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { ConfigModule } from '@nestjs/config'; import { ConfigModule } from '@nestjs/config';
import { ParticipanteModule } from './participante/participante.module';
@Module({ @Module({
@@ -33,14 +34,18 @@ import { ConfigModule } from '@nestjs/config';
autoLoadEntities: true, // Carga automáticamente las entidades autoLoadEntities: true, // Carga automáticamente las entidades
}), }),
CuestionarioModule, forwardRef(() => CuestionarioModule),
forwardRef(() => SeccionModule),
forwardRef(() => PreguntaModule),
forwardRef(() => OpcionModule),
ParticipanteModule,
TipoCuestionarioModule, TipoCuestionarioModule,
CuestionarioSeccionModule, CuestionarioSeccionModule,
SeccionModule,
SeccionPreguntaModule, SeccionPreguntaModule,
PreguntaModule,
TipoPreguntaModule, TipoPreguntaModule,
OpcionModule, PreguntaOpcionModule, PreguntaOpcionModule,
RespuestaParticipanteCerradaModule, RespuestaParticipanteCerradaModule,
CuestionarioRespondidoModule, CuestionarioRespondidoModule,
RespuestaParticipanteAbiertaModule], RespuestaParticipanteAbiertaModule],
+195 -13
View File
@@ -1,34 +1,216 @@
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; import {
Controller,
Post,
Body,
Get,
Param,
Put,
Delete,
HttpStatus,
HttpException,
Query,
} from '@nestjs/common';
import { CuestionarioService } from './cuestionario.service'; import { CuestionarioService } from './cuestionario.service';
import { CreateCuestionarioDto } from './dto/create-cuestionario.dto'; import { CreateCuestionarioDto } from './dto/create-cuestionario.dto';
import { UpdateCuestionarioDto } from './dto/update-cuestionario.dto'; import { UpdateCuestionarioDto } from './dto/update-cuestionario.dto';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiParam,
ApiBody,
ApiQuery,
} from '@nestjs/swagger';
import { SeccionExistenteCuestionarioDto } from './dto/seccion-existente-cuestionario.dto';
@Controller('cuestionario') @ApiTags('Cuestionarios')
@Controller('cuestionarios')
export class CuestionarioController { export class CuestionarioController {
constructor(private readonly cuestionarioService: CuestionarioService) {} constructor(private readonly cuestionarioService: CuestionarioService) {}
@Post() @Post()
create(@Body() createCuestionarioDto: CreateCuestionarioDto) { @ApiOperation({ summary: 'Crear un nuevo cuestionario' })
return this.cuestionarioService.create(createCuestionarioDto); @ApiBody({ type: CreateCuestionarioDto })
@ApiResponse({
status: HttpStatus.CREATED,
description: 'Cuestionario creado exitosamente con sus secciones y preguntas',
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description: 'Datos de entrada inválidos',
})
async create(@Body() createCuestionarioDto: CreateCuestionarioDto) {
try {
return await this.cuestionarioService.create(createCuestionarioDto);
} catch (error) {
throw new HttpException(
{
status: HttpStatus.BAD_REQUEST,
error: error.message,
},
HttpStatus.BAD_REQUEST,
);
}
} }
@Get() @Get()
findAll() { @ApiOperation({ summary: 'Obtener todos los cuestionarios' })
@ApiQuery({
name: 'tipo',
required: false,
description: 'Filtrar por tipo de cuestionario',
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Lista de cuestionarios obtenida exitosamente',
})
async findAll(@Query('tipo') tipoCuestionarioId?: number) {
if (tipoCuestionarioId) {
return this.cuestionarioService.findAllByTipo(tipoCuestionarioId);
}
return this.cuestionarioService.findAll(); return this.cuestionarioService.findAll();
} }
@Get(':id') @Get(':id')
findOne(@Param('id') id: string) { @ApiOperation({ summary: 'Obtener un cuestionario específico con todas sus relaciones' })
return this.cuestionarioService.findOne(+id); @ApiParam({ name: 'id', description: 'ID del cuestionario' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Cuestionario encontrado con todas sus secciones y preguntas',
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Cuestionario no encontrado',
})
async findOne(@Param('id') id: string) {
const cuestionario = await this.cuestionarioService.findOneWithRelations(+id);
if (!cuestionario) {
throw new HttpException(
'Cuestionario no encontrado',
HttpStatus.NOT_FOUND,
);
}
return cuestionario;
} }
@Patch(':id') @Put(':id')
update(@Param('id') id: string, @Body() updateCuestionarioDto: UpdateCuestionarioDto) { @ApiOperation({ summary: 'Actualizar los metadatos de un cuestionario' })
return this.cuestionarioService.update(+id, updateCuestionarioDto); @ApiParam({ name: 'id', description: 'ID del cuestionario a actualizar' })
@ApiBody({ type: UpdateCuestionarioDto })
@ApiResponse({
status: HttpStatus.OK,
description: 'Cuestionario actualizado exitosamente',
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Cuestionario no encontrado',
})
async update(
@Param('id') id: string,
@Body() updateCuestionarioDto: UpdateCuestionarioDto,
) {
try {
return await this.cuestionarioService.update(+id, updateCuestionarioDto);
} catch (error) {
throw new HttpException(
{
status: HttpStatus.BAD_REQUEST,
error: error.message,
},
HttpStatus.BAD_REQUEST,
);
}
} }
@Delete(':id') @Delete(':id')
remove(@Param('id') id: string) { @ApiOperation({ summary: 'Eliminar un cuestionario y todas sus relaciones' })
return this.cuestionarioService.remove(+id); @ApiParam({ name: 'id', description: 'ID del cuestionario a eliminar' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Cuestionario eliminado exitosamente',
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Cuestionario no encontrado',
})
async remove(@Param('id') id: string) {
try {
return await this.cuestionarioService.remove(+id);
} catch (error) {
throw new HttpException(
{
status: HttpStatus.BAD_REQUEST,
error: error.message,
},
HttpStatus.BAD_REQUEST,
);
}
} }
}
@Post(':id/secciones')
@ApiOperation({ summary: 'Agregar una sección existente al cuestionario' })
@ApiParam({ name: 'id', description: 'ID del cuestionario' })
@ApiBody({ type: SeccionExistenteCuestionarioDto })
@ApiResponse({
status: HttpStatus.CREATED,
description: 'Sección agregada al cuestionario exitosamente',
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Cuestionario o sección no encontrada',
})
async agregarSeccion(
@Param('id') cuestionarioId: string,
@Body() seccionDto: SeccionExistenteCuestionarioDto,
) {
try {
return await this.cuestionarioService.agregarSeccionExistente(
+cuestionarioId,
seccionDto.id_seccion,
seccionDto.posicion,
);
} catch (error) {
throw new HttpException(
{
status: error.status || HttpStatus.BAD_REQUEST,
error: error.message,
},
error.status || HttpStatus.BAD_REQUEST,
);
}
}
@Delete(':cuestionarioId/secciones/:seccionId')
@ApiOperation({ summary: 'Remover una sección del cuestionario' })
@ApiParam({ name: 'cuestionarioId', description: 'ID del cuestionario' })
@ApiParam({ name: 'seccionId', description: 'ID de la sección a remover' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Sección removida del cuestionario exitosamente',
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Relación no encontrada',
})
async removerSeccion(
@Param('cuestionarioId') cuestionarioId: string,
@Param('seccionId') seccionId: string,
) {
try {
return await this.cuestionarioService.eliminarSeccionDeCuestionario(
+cuestionarioId,
+seccionId,
);
} catch (error) {
throw new HttpException(
{
status: error.status || HttpStatus.BAD_REQUEST,
error: error.message,
},
error.status || HttpStatus.BAD_REQUEST,
);
}
}
}
+14 -3
View File
@@ -1,9 +1,20 @@
import { Module } from '@nestjs/common'; import { Module, forwardRef } from '@nestjs/common';
import { CuestionarioService } from './cuestionario.service'; import { TypeOrmModule } from '@nestjs/typeorm';
import { CuestionarioController } from './cuestionario.controller'; import { CuestionarioController } from './cuestionario.controller';
import { CuestionarioService } from './cuestionario.service';
import { Cuestionario } from './entities/cuestionario.entity';
import { CuestionarioSeccion } from '../cuestionario_seccion/entities/cuestionario_seccion.entity';
import { SeccionModule } from '../seccion/seccion.module';
import { TipoCuestionarioModule } from '../tipo_cuestionario/tipo_cuestionario.module';
@Module({ @Module({
imports: [
TypeOrmModule.forFeature([Cuestionario, CuestionarioSeccion]),
forwardRef(() => SeccionModule),
TipoCuestionarioModule,
],
controllers: [CuestionarioController], controllers: [CuestionarioController],
providers: [CuestionarioService], providers: [CuestionarioService],
exports: [CuestionarioService],
}) })
export class CuestionarioModule {} export class CuestionarioModule {}
+208 -13
View File
@@ -1,26 +1,221 @@
import { Injectable } from '@nestjs/common'; import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateCuestionarioDto } from './dto/create-cuestionario.dto'; import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Cuestionario } from './entities/cuestionario.entity';
import { SeccionService } from '../seccion/seccion.service';
import { CreateCuestionarioDto, SeccionEnCuestionarioDto } from './dto/create-cuestionario.dto';
import { UpdateCuestionarioDto } from './dto/update-cuestionario.dto'; import { UpdateCuestionarioDto } from './dto/update-cuestionario.dto';
import { Seccion } from '../seccion/entities/seccion.entity';
import { CuestionarioSeccion } from 'src/cuestionario_seccion/entities/cuestionario_seccion.entity';
import { TipoCuestionario } from 'src/tipo_cuestionario/entities/tipo_cuestionario.entity';
@Injectable() @Injectable()
export class CuestionarioService { export class CuestionarioService {
create(createCuestionarioDto: CreateCuestionarioDto) { async findAllByTipo(tipoCuestionarioId: number): Promise<Cuestionario[]> {
return 'This action adds a new cuestionario'; return this.cuestionarioRepository.find({
where: { tipo_cuestionario: { id_tipo_cuestionario: tipoCuestionarioId } },
relations: ['tipo_cuestionario', 'cuestionarioSecciones'],
});
}
obtenerResumen(arg0: number) {
throw new Error('Method not implemented.');
}
constructor(
@InjectRepository(Cuestionario)
private readonly cuestionarioRepository: Repository<Cuestionario>,
@InjectRepository(TipoCuestionario)
private readonly tipoCuestionarioRepository: Repository<TipoCuestionario>,
@InjectRepository(CuestionarioSeccion)
private readonly cuestionarioSeccionRepository: Repository<CuestionarioSeccion>,
private readonly seccionService: SeccionService,
) {}
async create(createCuestionarioDto: CreateCuestionarioDto) {
// Verificar tipo de cuestionario
const tipoCuestionario = await this.tipoCuestionarioRepository.findOne({
where: { id_tipo_cuestionario: createCuestionarioDto.id_tipo_cuestionario },
});
if (!tipoCuestionario) {
throw new NotFoundException('Tipo de cuestionario no encontrado');
}
// Crear cuestionario base
const cuestionario = this.cuestionarioRepository.create({
nombre_form: createCuestionarioDto.nombre_form,
descripcion: createCuestionarioDto.descripcion,
editable: createCuestionarioDto.editable ?? true,
fecha_inicio: createCuestionarioDto.fecha_inicio,
fecha_fin: createCuestionarioDto.fecha_fin,
tipo_cuestionario: tipoCuestionario,
contador_secciones: createCuestionarioDto.secciones?.length || 0,
});
const cuestionarioGuardado = await this.cuestionarioRepository.save(cuestionario);
// Procesar secciones si existen
if (createCuestionarioDto.secciones && createCuestionarioDto.secciones.length > 0) {
await this.agregarSeccionesACuestionario(
cuestionarioGuardado.id_cuestionario,
createCuestionarioDto.secciones,
);
}
return this.findOneWithRelations(cuestionarioGuardado.id_cuestionario);
} }
findAll() { private async agregarSeccionesACuestionario(
return `This action returns all cuestionario`; cuestionarioId: number,
secciones: SeccionEnCuestionarioDto[],
) {
await Promise.all(
secciones.map(async (seccionDto) => {
// Crear la sección usando el servicio existente
const seccionCreada = await this.seccionService.create(seccionDto.seccion);
// Crear la relación cuestionario-sección
const cuestionarioSeccion = this.cuestionarioSeccionRepository.create({
posicion: seccionDto.posicion,
cuestionario: { id_cuestionario: cuestionarioId },
seccion: { id_seccion: seccionCreada?.id_seccion },
});
await this.cuestionarioSeccionRepository.save(cuestionarioSeccion);
})
);
} }
findOne(id: number) { async findOneWithRelations(id: number) {
return `This action returns a #${id} cuestionario`; return this.cuestionarioRepository.findOne({
where: { id_cuestionario: id },
relations: [
'tipo_cuestionario',
'cuestionarioSecciones',
'cuestionarioSecciones.seccion',
'cuestionarioSecciones.seccion.seccionPreguntas',
'cuestionarioSecciones.seccion.seccionPreguntas.pregunta',
'cuestionarioSecciones.seccion.seccionPreguntas.pregunta.tipo_pregunta',
'cuestionarioSecciones.seccion.seccionPreguntas.pregunta.opciones',
'cuestionarioSecciones.seccion.seccionPreguntas.pregunta.opciones.opcion',
],
});
} }
update(id: number, updateCuestionarioDto: UpdateCuestionarioDto) { async findAll() {
return `This action updates a #${id} cuestionario`; return this.cuestionarioRepository.find({
relations: ['tipo_cuestionario'],
});
} }
remove(id: number) { async update(id: number, updateCuestionarioDto: UpdateCuestionarioDto) {
return `This action removes a #${id} cuestionario`; const cuestionario = await this.cuestionarioRepository.findOne({
where: { id_cuestionario: id },
});
if (!cuestionario) {
throw new NotFoundException(`Cuestionario con ID ${id} no encontrado`);
}
// Actualizar campos básicos
if (updateCuestionarioDto.nombre_form !== undefined) {
cuestionario.nombre_form = updateCuestionarioDto.nombre_form;
}
if (updateCuestionarioDto.descripcion !== undefined) {
cuestionario.descripcion = updateCuestionarioDto.descripcion;
}
if (updateCuestionarioDto.editable !== undefined) {
cuestionario.editable = updateCuestionarioDto.editable;
}
if (updateCuestionarioDto.fecha_inicio !== undefined) {
cuestionario.fecha_inicio = updateCuestionarioDto.fecha_inicio;
}
if (updateCuestionarioDto.fecha_fin !== undefined) {
cuestionario.fecha_fin = updateCuestionarioDto.fecha_fin;
}
return this.cuestionarioRepository.save(cuestionario);
} }
}
async remove(id: number) {
// Eliminar relaciones cuestionario-sección primero
await this.cuestionarioSeccionRepository.delete({
cuestionario: { id_cuestionario: id },
});
// Luego eliminar el cuestionario
const result = await this.cuestionarioRepository.delete(id);
if (result.affected === 0) {
throw new NotFoundException(`Cuestionario con ID ${id} no encontrado`);
}
return { affected: result.affected };
}
async agregarSeccionExistente(
cuestionarioId: number,
seccionId: number,
posicion: number,
) {
// Verificar que existan ambos elementos
const cuestionario = await this.cuestionarioRepository.findOne({
where: { id_cuestionario: cuestionarioId },
});
if (!cuestionario) {
throw new NotFoundException(`Cuestionario con ID ${cuestionarioId} no encontrado`);
}
const seccion = await this.seccionService.findOneWithRelations(seccionId);
if (!seccion) {
throw new NotFoundException(`Sección con ID ${seccionId} no encontrada`);
}
// Crear la relación
const cuestionarioSeccion = this.cuestionarioSeccionRepository.create({
posicion,
cuestionario: { id_cuestionario: cuestionarioId },
seccion: { id_seccion: seccionId },
});
await this.cuestionarioSeccionRepository.save(cuestionarioSeccion);
// Actualizar contador de secciones
await this.cuestionarioRepository.increment(
{ id_cuestionario: cuestionarioId },
'contador_secciones',
1,
);
return this.findOneWithRelations(cuestionarioId);
}
async eliminarSeccionDeCuestionario(cuestionarioId: number, seccionId: number) {
const relacion = await this.cuestionarioSeccionRepository.findOne({
where: {
cuestionario: { id_cuestionario: cuestionarioId },
seccion: { id_seccion: seccionId },
},
});
if (!relacion) {
throw new NotFoundException('La sección no está asociada a este cuestionario');
}
await this.cuestionarioSeccionRepository.remove(relacion);
// Actualizar contador de secciones
await this.cuestionarioRepository.decrement(
{ id_cuestionario: cuestionarioId },
'contador_secciones',
1,
);
return { success: true };
}
}
@@ -1 +1,46 @@
export class CreateCuestionarioDto {} import { IsNotEmpty, IsString, IsBoolean, IsDateString, IsNumber, IsOptional, IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { CreateSeccionDto } from 'src/seccion/dto/create-seccion.dto';
export class CreateCuestionarioDto {
@IsNotEmpty()
@IsString()
nombre_form: string;
@IsOptional()
@IsString()
descripcion?: string;
@IsOptional()
@IsBoolean()
editable?: boolean;
@IsNotEmpty()
@IsDateString()
fecha_inicio: Date;
@IsNotEmpty()
@IsDateString()
fecha_fin: Date;
@IsNotEmpty()
@IsNumber()
id_tipo_cuestionario: number;
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => SeccionEnCuestionarioDto)
secciones?: SeccionEnCuestionarioDto[];
}
export class SeccionEnCuestionarioDto {
@IsNotEmpty()
@IsNumber()
posicion: number;
@IsNotEmpty()
@ValidateNested()
@Type(() => CreateSeccionDto)
seccion: CreateSeccionDto;
}
@@ -0,0 +1,22 @@
import { IsNotEmpty, IsNumber, Min } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class SeccionExistenteCuestionarioDto {
@ApiProperty({
example: 1,
description: 'Posición de la sección dentro del cuestionario',
})
@IsNotEmpty()
@IsNumber()
@Min(1)
posicion: number;
@ApiProperty({
example: 5,
description: 'ID de la sección existente a agregar',
})
@IsNotEmpty()
@IsNumber()
@Min(1)
id_seccion: number;
}
@@ -1,4 +1,23 @@
import { PartialType } from '@nestjs/mapped-types'; import { IsOptional, IsString, IsBoolean, IsDateString } from 'class-validator';
import { CreateCuestionarioDto } from './create-cuestionario.dto';
export class UpdateCuestionarioDto extends PartialType(CreateCuestionarioDto) {} export class UpdateCuestionarioDto {
@IsOptional()
@IsString()
nombre_form?: string;
@IsOptional()
@IsString()
descripcion?: string;
@IsOptional()
@IsBoolean()
editable?: boolean;
@IsOptional()
@IsDateString()
fecha_inicio?: Date;
@IsOptional()
@IsDateString()
fecha_fin?: Date;
}
@@ -1,6 +1,7 @@
import { CuestionarioRespondido } from 'src/cuestionario_respondido/entities/cuestionario_respondido.entity';
import { CuestionarioSeccion } from 'src/cuestionario_seccion/entities/cuestionario_seccion.entity'; import { CuestionarioSeccion } from 'src/cuestionario_seccion/entities/cuestionario_seccion.entity';
import { TipoCuestionario } from 'src/tipo_cuestionario/entities/tipo_cuestionario.entity'; import { TipoCuestionario } from 'src/tipo_cuestionario/entities/tipo_cuestionario.entity';
import { Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToMany, ManyToOne } from 'typeorm'; import { Entity, PrimaryGeneratedColumn, Column, OneToMany, ManyToMany, ManyToOne, JoinColumn } from 'typeorm';
@@ -27,18 +28,22 @@ export class Cuestionario {
@Column({ type: 'datetime', nullable: true }) @Column({ type: 'datetime', nullable: true })
fecha_inicio: Date; fecha_inicio: Date;
@OneToMany(() => CuestionarioRespondido, cuestionarioRespondido => cuestionarioRespondido.cuestionario)
cuestionariosRespondidos: CuestionarioRespondido[];
@OneToMany(() => CuestionarioSeccion,(cuestionario_seccion) => cuestionario_seccion.id_cuestionario)
@OneToMany(() => CuestionarioSeccion,(cuestionario_seccion) => cuestionario_seccion.seccion)
@JoinColumn()
cuestionarioSeccion:CuestionarioSeccion[] cuestionarioSeccion:CuestionarioSeccion[]
@OneToMany(()=> Cuestionario, (cuestionario) => cuestionario.id_cuestionario) @OneToMany(()=> Cuestionario, (cuestionario) => cuestionario.cuestionario_original)
@Column({ type: 'int' }) cuestionarioOriginal: Cuestionario[];
id_cuestionario_original: number;
@OneToMany(()=> TipoCuestionario, (tipo_cuestionario) => tipo_cuestionario.id_tipo_cuestionario ) @ManyToOne(() => TipoCuestionario, (tipo) => tipo.cuestionarios)
@Column({ type: 'int' }) @JoinColumn({ name: 'id_tipo_cuestionario' }) // Clave foránea en la base de datos
id_tipo_cuestionario: number; tipo_cuestionario: TipoCuestionario;
@ManyToOne(()=> Cuestionario, (cuestionario) => cuestionario.id_cuestionario) @ManyToOne(()=> Cuestionario, (cuestionario) => cuestionario.cuestionarioOriginal)
Cuestionario: Cuestionario[]; @JoinColumn({ name: 'id_cuestionario_original' })
cuestionario_original: Cuestionario;
} }
@@ -1,9 +1,14 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { CuestionarioRespondidoService } from './cuestionario_respondido.service'; import { CuestionarioRespondidoService } from './cuestionario_respondido.service';
import { CuestionarioRespondidoController } from './cuestionario_respondido.controller'; import { CuestionarioRespondidoController } from './cuestionario_respondido.controller';
import { CuestionarioRespondido } from './entities/cuestionario_respondido.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({ @Module({
controllers: [CuestionarioRespondidoController],
providers: [CuestionarioRespondidoService], imports: [TypeOrmModule.forFeature([CuestionarioRespondido])],
controllers: [CuestionarioRespondidoController],
providers: [CuestionarioRespondidoService],
exports:[CuestionarioRespondidoService, TypeOrmModule]
}) })
export class CuestionarioRespondidoModule {} export class CuestionarioRespondidoModule {}
@@ -3,6 +3,7 @@ import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, JoinColum
import { Cuestionario } from 'src/cuestionario/entities/cuestionario.entity'; import { Cuestionario } from 'src/cuestionario/entities/cuestionario.entity';
import { RespuestaParticipanteCerrada } from 'src/respuesta_participante_cerrada/entities/respuesta_participante_cerrada.entity'; import { RespuestaParticipanteCerrada } from 'src/respuesta_participante_cerrada/entities/respuesta_participante_cerrada.entity';
import { RespuestaParticipanteAbierta } from 'src/respuesta_participante_abierta/entities/respuesta_participante_abierta.entity'; import { RespuestaParticipanteAbierta } from 'src/respuesta_participante_abierta/entities/respuesta_participante_abierta.entity';
import { Participante } from 'src/participante/participante.entity';
@Entity('cuestionario_respondido') @Entity('cuestionario_respondido')
export class CuestionarioRespondido { export class CuestionarioRespondido {
@@ -13,20 +14,20 @@ export class CuestionarioRespondido {
fechaRespuesta: Date; fechaRespuesta: Date;
// Relación con Participante // Relación con Participante
@ManyToOne(() => Participante, participante => participante.cuestionariosRespondidos) @ManyToOne(() => Participante, participante => participante.cuestionarioRespondidos)
@JoinColumn({ name: 'id_participante' }) @JoinColumn({ name: 'id_participante' })
id_participante: Participante; participante: Participante;
// Relación con Cuestionario // Relación con Cuestionario
@ManyToOne(() => Cuestionario, cuestionario => cuestionario.id_cuestionario) @ManyToOne(() => Cuestionario, cuestionario => cuestionario.cuestionariosRespondidos)
@JoinColumn({ name: 'id_cuestionario' }) @JoinColumn({ name: 'id_cuestionario' })
id_cuestionario: Cuestionario; cuestionario: Cuestionario;
// Relación con RespuestasCerradas // Relación con RespuestasCerradas
@OneToMany(() => RespuestaParticipanteCerrada, respuestaCerrada => respuestaCerrada.id_cuestionario_respondido) @OneToMany(() => RespuestaParticipanteCerrada, respuestaCerrada => respuestaCerrada.cuestionarioRespondido)
respuestasCerradas: RespuestaParticipanteCerrada[]; respuestasCerradas: RespuestaParticipanteCerrada[];
// Relación con RespuestasAbiertas // Relación con RespuestasAbiertas
@OneToMany(() => RespuestaParticipanteAbierta, respuestaAbierta => respuestaAbierta.id_cuestionario_respondido) @OneToMany(() => RespuestaParticipanteAbierta, respuestaAbierta => respuestaAbierta.cuestionarioRespondido)
respuestasAbiertas: RespuestaParticipanteAbierta[]; respuestasAbiertas: RespuestaParticipanteAbierta[];
} }
@@ -1,9 +1,13 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { CuestionarioSeccionService } from './cuestionario_seccion.service'; import { CuestionarioSeccionService } from './cuestionario_seccion.service';
import { CuestionarioSeccionController } from './cuestionario_seccion.controller'; import { CuestionarioSeccionController } from './cuestionario_seccion.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CuestionarioSeccion } from './entities/cuestionario_seccion.entity';
@Module({ @Module({
controllers: [CuestionarioSeccionController], imports: [TypeOrmModule.forFeature([CuestionarioSeccion])],
providers: [CuestionarioSeccionService], controllers: [CuestionarioSeccionController],
providers: [CuestionarioSeccionService],
exports:[CuestionarioSeccionService, TypeOrmModule]
}) })
export class CuestionarioSeccionModule {} export class CuestionarioSeccionModule {}
@@ -1,4 +1,4 @@
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
import { Cuestionario } from '../../cuestionario/entities/cuestionario.entity'; // Asegúrate de importar la entidad Cuestionario import { Cuestionario } from '../../cuestionario/entities/cuestionario.entity'; // Asegúrate de importar la entidad Cuestionario
import { Seccion } from 'src/seccion/entities/seccion.entity'; import { Seccion } from 'src/seccion/entities/seccion.entity';
@@ -11,11 +11,11 @@ export class CuestionarioSeccion {
posicion: number; posicion: number;
@ManyToOne(() => Cuestionario, (cuestionario) => cuestionario.id_cuestionario) @ManyToOne(() => Cuestionario, (cuestionario) => cuestionario.cuestionarioSeccion)
@Column({ type: 'int' }) @JoinColumn({name:'id_cuestionario'})
id_cuestionario: number; cuestionario: Cuestionario;
@ManyToOne(() => Seccion, (seccion) => seccion.id_seccion) @ManyToOne(() => Seccion, (seccion) => seccion.cuestionarioSeccion)
@Column({type:'int'}) @JoinColumn({name:'id_seccion'})
id_seccion:number; seccion:Seccion;
} }
+8 -8
View File
@@ -1,11 +1,11 @@
import { IsString, MaxLength } from "class-validator"; import { IsString, IsNumber, IsNotEmpty, IsOptional } from 'class-validator';
export class CreateOpcionDto { export class CreateOpcionDto {
@IsString()
@IsNotEmpty()
@IsString() opcion: string;
@MaxLength(50)
opcion: string;
@IsNumber()
} @IsOptional()
posicion?: number;
}
+10 -3
View File
@@ -1,4 +1,11 @@
import { PartialType } from '@nestjs/mapped-types'; import { IsString, IsNumber, IsOptional } from 'class-validator';
import { CreateOpcionDto } from './create-opcion.dto';
export class UpdateOpcionDto extends PartialType(CreateOpcionDto) {} export class UpdateOpcionDto {
@IsString()
@IsOptional()
opcion?: string;
@IsNumber()
@IsOptional()
posicion?: number;
}
+1 -1
View File
@@ -9,7 +9,7 @@ export class Opcion {
@Column({ type: String, nullable: false, length: 200, default: '' }) @Column({ type: String, nullable: false, length: 200, default: '' })
opcion: string; opcion: string;
@OneToMany(() => PreguntaOpcion, (preguntaOpcion) => preguntaOpcion.id_opcion) @OneToMany(() => PreguntaOpcion, (preguntaOpcion) => preguntaOpcion.opcion)
Preguntas: PreguntaOpcion[]; Preguntas: PreguntaOpcion[];
+19 -11
View File
@@ -2,33 +2,41 @@ import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/commo
import { OpcionService } from './opcion.service'; import { OpcionService } from './opcion.service';
import { CreateOpcionDto } from './dto/create-opcion.dto'; import { CreateOpcionDto } from './dto/create-opcion.dto';
import { UpdateOpcionDto } from './dto/update-opcion.dto'; import { UpdateOpcionDto } from './dto/update-opcion.dto';
import { Pregunta } from 'src/pregunta/entities/pregunta.entity';
@Controller('opcion') @Controller('opcion')
export class OpcionController { export class OpcionController {
constructor(private readonly opcionService: OpcionService) {} constructor(private readonly opcionService: OpcionService) {}
@Post() @Post()
create(@Body() createOpcionDto: CreateOpcionDto) { async create(
return this.opcionService.create(createOpcionDto); @Param('PreguntaId')PreguntaId:number,
@Body() createOpcionDto: CreateOpcionDto) {
return this.opcionService.create(PreguntaId,createOpcionDto);
} }
@Get() @Get()
findAll() { async findAll(
return this.opcionService.findAll(); @Param('PreguntaId')PreguntaId:number
) {
return this.opcionService.findAllByPregunta(PreguntaId);
} }
@Get(':id') @Get(':id')
findOne(@Param('id') id: string) { async findOne(
return this.opcionService.findOne(+id); @Param('preguntaId') preguntaId: number,
@Param('opcionId') OpcionId: number) {
return this.opcionService.findOne(preguntaId, OpcionId);
} }
@Patch(':id') @Patch(':id')
update(@Param('id') id: string, @Body() updateOpcionDto: UpdateOpcionDto) { async pdate(@Param('preguntaId') preguntaId:number ,@Param('opcionId') id: number, @Body() updateOpcionDto: UpdateOpcionDto) {
return this.opcionService.update(+id, updateOpcionDto); return this.opcionService.update(preguntaId, id,updateOpcionDto);
} }
@Delete(':id') @Delete(':id')
remove(@Param('id') id: string) { async remove(@Param('preguntaId') preguntaId:number ,@Param('opcionId') id: number) {
return this.opcionService.remove(+id); return this.opcionService.remove(preguntaId, id);
} }
} }
+12 -2
View File
@@ -1,9 +1,19 @@
import { Module } from '@nestjs/common'; import { Module, forwardRef } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { OpcionService } from './opcion.service'; import { OpcionService } from './opcion.service';
import { OpcionController } from './opcion.controller'; import { OpcionController } from './opcion.controller';
import { Opcion } from './entities/opcion.entity';
import { PreguntaOpcion } from '../pregunta_opcion/entities/pregunta_opcion.entity';
import { PreguntaModule } from '../pregunta/pregunta.module';
import { Pregunta } from '../pregunta/entities/pregunta.entity';
@Module({ @Module({
imports: [
TypeOrmModule.forFeature([Opcion, Pregunta, PreguntaOpcion]), // Importar todas las entidades necesarias
forwardRef(() => PreguntaModule), // Usar forwardRef para dependencia circular
],
controllers: [OpcionController], controllers: [OpcionController],
providers: [OpcionService], providers: [OpcionService],
exports: [OpcionService, TypeOrmModule], // Exportar TypeOrmModule si otros módulos necesitan los repositorios
}) })
export class OpcionModule {} export class OpcionModule {}
+139 -17
View File
@@ -1,36 +1,158 @@
import { Injectable } from '@nestjs/common'; import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateOpcionDto } from './dto/create-opcion.dto';
import { UpdateOpcionDto } from './dto/update-opcion.dto';
import { Opcion } from './entities/opcion.entity';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { Opcion } from './entities/opcion.entity';
import { Pregunta } from '../pregunta/entities/pregunta.entity';
import { CreateOpcionDto } from './dto/create-opcion.dto';
import { PreguntaOpcion } from 'src/pregunta_opcion/entities/pregunta_opcion.entity';
import { UpdateOpcionDto } from './dto/update-opcion.dto';
@Injectable() @Injectable()
export class OpcionService { export class OpcionService {
constructor( constructor(
@InjectRepository(Opcion) @InjectRepository(Opcion)
private repository: Repository<Opcion>, private readonly opcionRepository: Repository<Opcion>,
){} @InjectRepository(Pregunta)
private readonly preguntaRepository: Repository<Pregunta>,
@InjectRepository(PreguntaOpcion)
private readonly preguntaOpcionRepository: Repository<PreguntaOpcion>,
) {}
async create(preguntaId: number, createOpcionDto: CreateOpcionDto) {
// Verificar que la pregunta exista y sea cerrada
const pregunta = await this.preguntaRepository.findOne({
where: { id_pregunta: preguntaId },
relations: ['tipo_pregunta'],
});
if (!pregunta) {
throw new NotFoundException('Pregunta no encontrada');
}
create():Promise<Opcion> { if (pregunta.tipo_pregunta.tipo_pregunta !== 'Cerrada') {
return this.repository.save(this.repository.create()); throw new Error('Solo se pueden agregar opciones a preguntas cerradas');
}
// Crear la opción
const opcion = this.opcionRepository.create({
opcion: createOpcionDto.opcion,
});
const savedOpcion = await this.opcionRepository.save(opcion);
// Crear la relación entre pregunta y opción
const preguntaOpcion = this.preguntaOpcionRepository.create({
pregunta: { id_pregunta: preguntaId },
opcion: savedOpcion,
posicion: createOpcionDto.posicion || 0, // Asignar posición
});
await this.preguntaOpcionRepository.save(preguntaOpcion);
// Actualizar el contador de opciones en la pregunta
await this.preguntaRepository.increment(
{ id_pregunta: preguntaId },
'contador_opcion',
1
);
return savedOpcion;
} }
findAll() { async findAllByPregunta(preguntaId: number) {
return `This action returns all opcion`; return this.preguntaOpcionRepository.find({
where: { pregunta: { id_pregunta: preguntaId } },
relations: ['opcion'],
order: { posicion: 'ASC' },
});
} }
findOne(id: number) { async findOne(preguntaId: number, id: number) {
return `This action returns a #${id} opcion`; const preguntaOpcion = await this.preguntaOpcionRepository.findOne({
where: {
pregunta: { id_pregunta: preguntaId },
opcion: { id_opcion: id },
},
relations: ['opcion'],
});
if (!preguntaOpcion) {
throw new NotFoundException('Opción no encontrada para esta pregunta');
}
return preguntaOpcion.opcion;
} }
update(id: number, updateOpcionDto: UpdateOpcionDto) { async update(
return `This action updates a #${id} opcion`; preguntaId: number,
id: number,
updateOpcionDto: UpdateOpcionDto
) {
// Verificar que la relación exista
const preguntaOpcion = await this.preguntaOpcionRepository.findOne({
where: {
pregunta: { id_pregunta: preguntaId },
opcion: { id_opcion: id },
},
relations: ['opcion'],
});
if (!preguntaOpcion) {
throw new NotFoundException('Opción no encontrada para esta pregunta');
}
// Actualizar posición si se proporciona
if (updateOpcionDto.posicion !== undefined) {
await this.preguntaOpcionRepository.update(
{ id_pregunta_opcion: preguntaOpcion.id_pregunta_opcion },
{ posicion: updateOpcionDto.posicion }
);
}
// Actualizar texto de la opción
if (updateOpcionDto.opcion) {
await this.opcionRepository.update(
{ id_opcion: id },
{ opcion: updateOpcionDto.opcion }
);
}
return this.findOne(preguntaId, id);
} }
remove(id: number) { async remove(preguntaId: number, id: number) {
return `This action removes a #${id} opcion`; // Verificar que la relación exista
const preguntaOpcion = await this.preguntaOpcionRepository.findOne({
where: {
pregunta: { id_pregunta: preguntaId },
opcion: { id_opcion: id },
},
});
if (!preguntaOpcion) {
throw new NotFoundException('Opción no encontrada para esta pregunta');
}
// Eliminar la relación primero
await this.preguntaOpcionRepository.remove(preguntaOpcion);
// Eliminar la opción (si no está siendo usada en otras preguntas)
const count = await this.preguntaOpcionRepository.count({
where: { opcion: { id_opcion: id } },
});
if (count === 0) {
await this.opcionRepository.delete(id);
}
// Actualizar el contador de opciones en la pregunta
await this.preguntaRepository.decrement(
{ id_pregunta: preguntaId },
'contador_opcion',
1
);
return { message: 'Opción eliminada correctamente' };
} }
} }
+5
View File
@@ -1,3 +1,4 @@
import { CuestionarioRespondido } from "src/cuestionario_respondido/entities/cuestionario_respondido.entity";
import { ParticipanteEvento } from "src/participante_evento/participante_evento.entity"; import { ParticipanteEvento } from "src/participante_evento/participante_evento.entity";
import { TipoUser } from "src/tipo_user/tipo_user.entity"; import { TipoUser } from "src/tipo_user/tipo_user.entity";
import { Column, Entity, JoinColumn, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Column, Entity, JoinColumn, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
@@ -13,6 +14,10 @@ export class Participante {
@Column() @Column()
id_tipo_user: number id_tipo_user: number
@OneToMany(() => CuestionarioRespondido, cuestionarioRespondido => cuestionarioRespondido.participante )
cuestionarioRespondidos:CuestionarioRespondido[];
//Relacion con tipo usuario //Relacion con tipo usuario
@ManyToOne(() => TipoUser, tipoUser => tipoUser.participante) @ManyToOne(() => TipoUser, tipoUser => tipoUser.participante)
@JoinColumn({ name: 'participante_id' }) //nombre de la relacion @JoinColumn({ name: 'participante_id' }) //nombre de la relacion
+1 -1
View File
@@ -8,6 +8,6 @@ import { Participante } from './participante.entity';
imports: [TypeOrmModule.forFeature([Participante])], imports: [TypeOrmModule.forFeature([Participante])],
controllers: [ParticipanteController], controllers: [ParticipanteController],
providers: [ParticipanteService], providers: [ParticipanteService],
exports: [ParticipanteService], exports: [ParticipanteService, TypeOrmModule],
}) })
export class ParticipanteModule {} export class ParticipanteModule {}
+1 -1
View File
@@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { Participante } from './participante.entity'; import { Participante } from './participante.entity';
import { Repository } from 'typeorm'; import { Repository } from 'typeorm';
import { CreateParticipanteDto } from './dto/create-participante.dto'; import { CreateParticipanteDto } from './dto/create-participante.dto';
import { UpdateAdminDto } from 'src/admin/dto/update.admin.dto';
import { UpdateParticipanteDto } from './dto/update.participante.dto'; import { UpdateParticipanteDto } from './dto/update.participante.dto';
@Injectable() @Injectable()
@@ -8,6 +8,7 @@ import { Evento } from 'src/evento/evento.entity';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([ParticipanteEvento]), Evento], imports: [TypeOrmModule.forFeature([ParticipanteEvento]), Evento],
controllers: [ParticipanteEventoController], controllers: [ParticipanteEventoController],
providers: [ParticipanteEventoService] providers: [ParticipanteEventoService],
exports:[TypeOrmModule,ParticipanteEventoService]
}) })
export class ParticipanteEventoModule {} export class ParticipanteEventoModule {}
+23 -1
View File
@@ -1 +1,23 @@
export class CreatePreguntaDto {} import { IsNotEmpty, IsString, IsBoolean, IsNumber, IsOptional, IsArray, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { CreateOpcionDto } from '../../opcion/dto/create-opcion.dto';
export class CreatePreguntaDto {
@IsNotEmpty()
@IsString()
pregunta: string;
@IsOptional()
@IsBoolean()
obligatoria?: boolean;
@IsNotEmpty()
@IsNumber()
id_tipo_pregunta: number;
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => CreateOpcionDto)
opciones?: CreateOpcionDto[];
}
+14 -3
View File
@@ -1,4 +1,15 @@
import { PartialType } from '@nestjs/mapped-types'; import { IsOptional, IsString, IsBoolean, IsNumber } from 'class-validator';
import { CreatePreguntaDto } from './create-pregunta.dto';
export class UpdatePreguntaDto extends PartialType(CreatePreguntaDto) {} export class UpdatePreguntaDto {
@IsOptional()
@IsString()
pregunta?: string;
@IsOptional()
@IsBoolean()
obligatoria?: boolean;
@IsOptional()
@IsNumber()
id_tipo_pregunta?: number;
}
+18 -13
View File
@@ -1,33 +1,38 @@
import { PreguntaOpcion } from 'src/pregunta_opcion/entities/pregunta_opcion.entity'; import { PreguntaOpcion } from 'src/pregunta_opcion/entities/pregunta_opcion.entity';
import { RespuestaParticipanteAbierta } from 'src/respuesta_participante_abierta/entities/respuesta_participante_abierta.entity';
import { SeccionPregunta } from 'src/seccion_pregunta/entities/seccion_pregunta.entity'; import { SeccionPregunta } from 'src/seccion_pregunta/entities/seccion_pregunta.entity';
import { TipoPregunta } from 'src/tipo_pregunta/entities/tipo_pregunta.entity'; import { TipoPregunta } from 'src/tipo_pregunta/entities/tipo_pregunta.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, JoinColumn } from 'typeorm'; import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, JoinColumn } from 'typeorm';
@Entity() @Entity('pregunta')
export class Pregunta { export class Pregunta {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn({ name: 'id_pregunta' })
id_pregunta: number; id_pregunta: number;
@Column() @Column({ type: 'text' })
pregunta: string; pregunta: string;
@Column({ default: 0 }) @Column({ name: 'contador_opcion', default: 0 })
contador_opcion: number; contador_opcion: number;
@Column({ default: false }) @Column({ default: false })
obligatoria: boolean; obligatoria: boolean;
@ManyToOne(() => TipoPregunta, tipo => tipo.preguntas) // Relación con TipoPregunta
@ManyToOne(() => TipoPregunta, tipoPregunta => tipoPregunta.preguntas)
@JoinColumn({ name: 'id_tipo_pregunta' }) @JoinColumn({ name: 'id_tipo_pregunta' })
id_tipo_pregunta: TipoPregunta; tipo_pregunta: TipoPregunta;
@Column({ nullable: true }) // Relación con PreguntaOpcion (tabla intermedia para opciones)
id_opcion_dependiente: number; @OneToMany(() => PreguntaOpcion, preguntaOpcion => preguntaOpcion.pregunta)
@OneToMany(() => SeccionPregunta, seccionPregunta => seccionPregunta.id_pregunta)
seccionPreguntas: SeccionPregunta[];
@OneToMany(() => PreguntaOpcion, preguntaOpcion => preguntaOpcion.id_pregunta)
opciones: PreguntaOpcion[]; opciones: PreguntaOpcion[];
// Relación con SeccionPregunta (tabla intermedia para secciones)
@OneToMany(() => SeccionPregunta, seccionPregunta => seccionPregunta.pregunta)
seccionesPregunta: SeccionPregunta[];
// Relación con RespuestasAbiertas
@OneToMany(() => RespuestaParticipanteAbierta, respuestaAbierta => respuestaAbierta.pregunta)
respuestasAbiertas: RespuestaParticipanteAbierta[];
} }
+89 -13
View File
@@ -1,34 +1,110 @@
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; import {
Controller,
Post,
Body,
Get,
Param,
Put,
Delete,
HttpStatus,
HttpException,
Query
} from '@nestjs/common';
import { PreguntaService } from './pregunta.service'; import { PreguntaService } from './pregunta.service';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger';
import { CreatePreguntaDto } from './dto/create-pregunta.dto'; import { CreatePreguntaDto } from './dto/create-pregunta.dto';
import { UpdatePreguntaDto } from './dto/update-pregunta.dto'; import { UpdatePreguntaDto } from './dto/update-pregunta.dto';
@Controller('pregunta') @ApiTags('Preguntas')
@Controller('preguntas')
export class PreguntaController { export class PreguntaController {
constructor(private readonly preguntaService: PreguntaService) {} constructor(private readonly preguntaService: PreguntaService) {}
@Post() @Post()
create(@Body() createPreguntaDto: CreatePreguntaDto) { @ApiOperation({ summary: 'Crear una nueva pregunta' })
return this.preguntaService.create(createPreguntaDto); @ApiResponse({ status: 201, description: 'Pregunta creada exitosamente' })
@ApiResponse({ status: 400, description: 'Datos de entrada inválidos' })
@ApiResponse({ status: 404, description: 'Tipo de pregunta no encontrado' })
async create(@Body() createPreguntaDto: CreatePreguntaDto) {
try {
return await this.preguntaService.create(createPreguntaDto);
} catch (error) {
throw new HttpException({
status: HttpStatus.BAD_REQUEST,
error: error.message,
}, HttpStatus.BAD_REQUEST);
}
} }
@Get() @Get()
findAll() { @ApiOperation({ summary: 'Obtener todas las preguntas' })
@ApiQuery({ name: 'tipo', required: false, description: 'Filtrar por tipo de pregunta' })
@ApiResponse({ status: 200, description: 'Lista de preguntas' })
async findAll(@Query('tipo') tipoPreguntaId?: number) {
if (tipoPreguntaId) {
return this.preguntaService.findAllByTipo(tipoPreguntaId);
}
return this.preguntaService.findAll(); return this.preguntaService.findAll();
} }
@Get(':id') @Get(':id')
findOne(@Param('id') id: string) { @ApiOperation({ summary: 'Obtener una pregunta específica' })
return this.preguntaService.findOne(+id); @ApiParam({ name: 'id', description: 'ID de la pregunta' })
@ApiResponse({ status: 200, description: 'Pregunta encontrada' })
@ApiResponse({ status: 404, description: 'Pregunta no encontrada' })
async findOne(@Param('id') id: string) {
const pregunta = await this.preguntaService.findOneWithRelations(+id);
if (!pregunta) {
throw new HttpException('Pregunta no encontrada', HttpStatus.NOT_FOUND);
}
return pregunta;
} }
@Patch(':id') @Put(':id')
update(@Param('id') id: string, @Body() updatePreguntaDto: UpdatePreguntaDto) { @ApiOperation({ summary: 'Actualizar una pregunta' })
return this.preguntaService.update(+id, updatePreguntaDto); @ApiParam({ name: 'id', description: 'ID de la pregunta a actualizar' })
@ApiResponse({ status: 200, description: 'Pregunta actualizada' })
@ApiResponse({ status: 404, description: 'Pregunta no encontrada' })
async update(
@Param('id') id: string,
@Body() updatePreguntaDto: UpdatePreguntaDto,
) {
try {
return await this.preguntaService.update(+id, updatePreguntaDto);
} catch (error) {
throw new HttpException({
status: HttpStatus.BAD_REQUEST,
error: error.message,
}, HttpStatus.BAD_REQUEST);
}
} }
@Delete(':id') @Delete(':id')
remove(@Param('id') id: string) { @ApiOperation({ summary: 'Eliminar una pregunta' })
return this.preguntaService.remove(+id); @ApiParam({ name: 'id', description: 'ID de la pregunta a eliminar' })
@ApiResponse({ status: 200, description: 'Pregunta eliminada' })
@ApiResponse({ status: 404, description: 'Pregunta no encontrada' })
async remove(@Param('id') id: string) {
try {
return await this.preguntaService.remove(+id);
} catch (error) {
throw new HttpException({
status: HttpStatus.BAD_REQUEST,
error: error.message,
}, HttpStatus.BAD_REQUEST);
}
} }
}
@Get(':id/opciones')
@ApiOperation({ summary: 'Obtener opciones de una pregunta' })
@ApiParam({ name: 'id', description: 'ID de la pregunta' })
@ApiResponse({ status: 200, description: 'Opciones de la pregunta' })
@ApiResponse({ status: 404, description: 'Pregunta no encontrada' })
async getOpciones(@Param('id') id: string) {
const pregunta = await this.preguntaService.findOneWithRelations(+id);
if (!pregunta) {
throw new HttpException('Pregunta no encontrada', HttpStatus.NOT_FOUND);
}
return pregunta.opciones;
}
}
+13 -2
View File
@@ -1,9 +1,20 @@
import { Module } from '@nestjs/common'; import { Module, forwardRef } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Pregunta } from './entities/pregunta.entity';
import { PreguntaService } from './pregunta.service'; import { PreguntaService } from './pregunta.service';
import { PreguntaController } from './pregunta.controller'; import { PreguntaController } from './pregunta.controller';
import { OpcionModule } from '../opcion/opcion.module';
import { PreguntaOpcion } from '../pregunta_opcion/entities/pregunta_opcion.entity';
import { TipoPreguntaModule } from '../tipo_pregunta/tipo_pregunta.module';
@Module({ @Module({
imports: [
TypeOrmModule.forFeature([Pregunta, PreguntaOpcion]),
forwardRef(() => OpcionModule),
TipoPreguntaModule,
],
controllers: [PreguntaController], controllers: [PreguntaController],
providers: [PreguntaService], providers: [PreguntaService],
exports: [PreguntaService],
}) })
export class PreguntaModule {} export class PreguntaModule {}
+148 -17
View File
@@ -1,26 +1,157 @@
import { Injectable } from '@nestjs/common'; import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Pregunta } from './entities/pregunta.entity';
import { OpcionService } from '../opcion/opcion.service';
import { CreatePreguntaDto } from './dto/create-pregunta.dto'; import { CreatePreguntaDto } from './dto/create-pregunta.dto';
import { UpdatePreguntaDto } from './dto/update-pregunta.dto'; import { CreateOpcionDto } from '../opcion/dto/create-opcion.dto';
import { TipoPregunta } from 'src/tipo_pregunta/entities/tipo_pregunta.entity';
@Injectable() @Injectable()
export class PreguntaService { export class PreguntaService {
create(createPreguntaDto: CreatePreguntaDto) {
return 'This action adds a new pregunta'; constructor(
@InjectRepository(Pregunta)
private readonly preguntaRepository: Repository<Pregunta>,
@InjectRepository(TipoPregunta)
private readonly tipoPreguntaRepository: Repository<TipoPregunta>,
private readonly opcionService: OpcionService,
) {}
async create(createPreguntaDto: CreatePreguntaDto) {
// Verificar que el tipo de pregunta exista
//Se espera que solo exista el ripo de pregunta abierta y cerrada
const tipoPregunta = await this.tipoPreguntaRepository.findOne({
where: { id_tipo: createPreguntaDto.id_tipo_pregunta },
});
if (!tipoPregunta) {
throw new NotFoundException(
`Tipo de pregunta con ID ${createPreguntaDto.id_tipo_pregunta} no encontrado`,
);
}
// Crear la pregunta base
const pregunta = this.preguntaRepository.create({
pregunta: createPreguntaDto.pregunta,
obligatoria: createPreguntaDto.obligatoria || false,
tipo_pregunta: tipoPregunta,
});
const preguntaGuardada = await this.preguntaRepository.save(pregunta);
// Si es pregunta cerrada y tiene opciones, crearlas
if (
tipoPregunta.tipo_pregunta === 'Cerrada' &&
createPreguntaDto.opciones &&
createPreguntaDto.opciones.length > 0
) {
await this.agregarOpcionesAPregunta(
preguntaGuardada.id_pregunta,
createPreguntaDto.opciones,
);
// Actualizar el contador de opciones
await this.preguntaRepository.update(
{ id_pregunta: preguntaGuardada.id_pregunta },
{ contador_opcion: createPreguntaDto.opciones.length },
);
}
return this.findOneWithRelations(preguntaGuardada.id_pregunta);
}
private async agregarOpcionesAPregunta(
preguntaId: number,
opciones: CreateOpcionDto[],
) {
for (const opcionDto of opciones) {
await this.opcionService.create(preguntaId, opcionDto);
}
}
async findOneWithRelations(id: number) {
return this.preguntaRepository.findOne({
where: { id_pregunta: id },
relations: [
'tipo_pregunta',
'opciones',
'opciones.opcion',
'seccionesPregunta',
'seccionesPregunta.seccion',
],
});
}
async findAllByTipo(tipoPreguntaId: number) {
return this.preguntaRepository.find({
where: { tipo_pregunta: { id_tipo: tipoPreguntaId } },
relations: ['opciones', 'opciones.opcion'],
});
}
async remove(id: number) {
const pregunta = await this.preguntaRepository.findOne({
where: { id_pregunta: id },
relations: ['opciones'],
});
if (!pregunta) {
throw new NotFoundException(`Pregunta con ID ${id} no encontrada`);
}
// Eliminar opciones primero si existen
if (pregunta.opciones && pregunta.opciones.length > 0) {
for (const opcion of pregunta.opciones) {
await this.opcionService.remove(id, opcion.id_pregunta_opcion);
}
}
// Eliminar la pregunta
await this.preguntaRepository.delete(id);
return { message: 'Pregunta eliminada correctamente' };
}
async update(id: number, updatePreguntaDto: any) {
const pregunta = await this.preguntaRepository.findOne({
where: { id_pregunta: id },
});
if (!pregunta) {
throw new NotFoundException(`Pregunta con ID ${id} no encontrada`);
}
// Actualizar campos básicos
if (updatePreguntaDto.pregunta !== undefined) {
pregunta.pregunta = updatePreguntaDto.pregunta;
}
if (updatePreguntaDto.obligatoria !== undefined) {
pregunta.obligatoria = updatePreguntaDto.obligatoria;
}
// Si se cambia el tipo de pregunta
if (updatePreguntaDto.id_tipo_pregunta !== undefined) {
const tipoPregunta = await this.tipoPreguntaRepository.findOne({
where: { id_tipo: updatePreguntaDto.id_tipo_pregunta },
});
if (!tipoPregunta) {
throw new NotFoundException(
`Tipo de pregunta con ID ${updatePreguntaDto.id_tipo_pregunta} no encontrado`,
);
}
pregunta.tipo_pregunta = tipoPregunta;
}
return this.preguntaRepository.save(pregunta);
} }
findAll() { findAll() {
return `This action returns all pregunta`; return this.preguntaRepository.find();
} }
findOne(id: number) { }
return `This action returns a #${id} pregunta`;
}
update(id: number, updatePreguntaDto: UpdatePreguntaDto) {
return `This action updates a #${id} pregunta`;
}
remove(id: number) {
return `This action removes a #${id} pregunta`;
}
}
@@ -1,25 +1,31 @@
import { Opcion } from 'src/opcion/entities/opcion.entity'; import { Opcion } from 'src/opcion/entities/opcion.entity';
import { Pregunta } from 'src/pregunta/entities/pregunta.entity'; import { Pregunta } from 'src/pregunta/entities/pregunta.entity';
import { RespuestaParticipanteCerrada } from 'src/respuesta_participante_cerrada/entities/respuesta_participante_cerrada.entity'; import { RespuestaParticipanteCerrada } from 'src/respuesta_participante_cerrada/entities/respuesta_participante_cerrada.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany } from 'typeorm'; import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, JoinColumn } from 'typeorm';
@Entity() @Entity('pregunta_opcion')
export class PreguntaOpcion { export class PreguntaOpcion {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn({ name: 'id_pregunta_opcion' })
id_pregunta_opcion: number; id_pregunta_opcion: number;
@Column() @Column()
posicion: number; posicion: number;
@ManyToOne(() => Pregunta, pregunta => pregunta.id_pregunta) // Relación con Pregunta
@Column() @ManyToOne(() => Pregunta, pregunta => pregunta.opciones)
id_pregunta: Pregunta; @JoinColumn({ name: 'id_pregunta' })
pregunta: Pregunta;
@ManyToOne(() => Opcion, opcion => opcion.id_opcion) // Relación con Opcion
@Column() @ManyToOne(() => Opcion, opcion => opcion.Preguntas)
id_opcion: Opcion; @JoinColumn({ name: 'id_opcion' })
opcion: Opcion;
@OneToMany(()=> RespuestaParticipanteCerrada, respuestaParticipanteCerrada=>respuestaParticipanteCerrada.id_pregunta_opcion) // Nueva relación con RespuestaParticipanteCerrada
respuestaParticipanteCerrada:RespuestaParticipanteCerrada[]; @OneToMany(
() => RespuestaParticipanteCerrada,
respuestaCerrada => respuestaCerrada.preguntaOpcion
)
respuestasCerradas: RespuestaParticipanteCerrada[];
} }
@@ -1,9 +1,13 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { PreguntaOpcionService } from './pregunta_opcion.service'; import { PreguntaOpcionService } from './pregunta_opcion.service';
import { PreguntaOpcionController } from './pregunta_opcion.controller'; import { PreguntaOpcionController } from './pregunta_opcion.controller';
import { PreguntaOpcion } from './entities/pregunta_opcion.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({ @Module({
controllers: [PreguntaOpcionController], imports: [TypeOrmModule.forFeature([PreguntaOpcion])],
providers: [PreguntaOpcionService], controllers: [PreguntaOpcionController],
providers: [PreguntaOpcionService],
exports:[PreguntaOpcionService, TypeOrmModule]
}) })
export class PreguntaOpcionModule {} export class PreguntaOpcionModule {}
@@ -2,29 +2,45 @@ import { CuestionarioRespondido } from 'src/cuestionario_respondido/entities/cue
import { Pregunta } from 'src/pregunta/entities/pregunta.entity'; import { Pregunta } from 'src/pregunta/entities/pregunta.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm'; import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
@Entity('respuesta_participante_abierta') @Entity('respuesta_participante_abierta')
export class RespuestaParticipanteAbierta { export class RespuestaParticipanteAbierta {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn({
idRespuestaParticipanteAbierta: number; name: 'id_respuesta_participante_abierta',
type: 'integer'
})
id: number;
@Column({ type: 'text' }) @Column({
type: 'text',
nullable: false
})
respuesta: string; respuesta: string;
// Relación con CuestionarioRespondido // Relación con CuestionarioRespondido
@ManyToOne( @ManyToOne(
() => CuestionarioRespondido, () => CuestionarioRespondido,
cuestionarioRespondido => cuestionarioRespondido.idCuestionarioRespondido (cuestionarioRespondido) => cuestionarioRespondido.respuestasAbiertas
) )
@JoinColumn({ name: 'id_cuestionario_respondido' }) @JoinColumn({
id_cuestionario_respondido: CuestionarioRespondido; name: 'id_cuestionario_respondido'
})
cuestionarioRespondido: CuestionarioRespondido;
// Relación con Pregunta
@ManyToOne( @ManyToOne(
() => Pregunta, () => Pregunta,
pregunta => pregunta.id_pregunta (pregunta) => pregunta.respuestasAbiertas
) )
@JoinColumn({ name: 'id_pregunta' }) @JoinColumn({
id_pregunta: Pregunta; name: 'id_pregunta'
})
pregunta: Pregunta;
/* Campo adicional para tracking
@Column({
name: 'fecha_respuesta',
type: 'timestamp',
default: () => 'CURRENT_TIMESTAMP'
})
fechaRespuesta: Date;*/
} }
@@ -1,9 +1,15 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { RespuestaParticipanteAbiertaService } from './respuesta_participante_abierta.service'; import { RespuestaParticipanteAbiertaService } from './respuesta_participante_abierta.service';
import { RespuestaParticipanteAbiertaController } from './respuesta_participante_abierta.controller'; import { RespuestaParticipanteAbiertaController } from './respuesta_participante_abierta.controller';
import { RespuestaParticipanteAbierta } from './entities/respuesta_participante_abierta.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({ @Module({
controllers: [RespuestaParticipanteAbiertaController], imports: [
providers: [RespuestaParticipanteAbiertaService], TypeOrmModule.forFeature([RespuestaParticipanteAbierta]),
],
controllers: [RespuestaParticipanteAbiertaController],
providers: [RespuestaParticipanteAbiertaService],
exports: [RespuestaParticipanteAbiertaService, TypeOrmModule],
}) })
export class RespuestaParticipanteAbiertaModule {} export class RespuestaParticipanteAbiertaModule {}
@@ -10,11 +10,11 @@ export class RespuestaParticipanteCerrada {
@ManyToOne(() => CuestionarioRespondido, cuestionarioRespondido => cuestionarioRespondido.respuestasCerradas) @ManyToOne(() => CuestionarioRespondido, cuestionarioRespondido => cuestionarioRespondido.respuestasCerradas)
@JoinColumn({ name: 'id_cuestionario_respondido' }) @JoinColumn({ name: 'id_cuestionario_respondido' })
id_cuestionario_respondido: CuestionarioRespondido; cuestionarioRespondido: CuestionarioRespondido;
@ManyToOne(() => PreguntaOpcion, preguntaOpcion => preguntaOpcion.id_pregunta_opcion) @ManyToOne(() => PreguntaOpcion, preguntaOpcion => preguntaOpcion.respuestasCerradas)
@JoinColumn({ name: 'id_pregunta_opcion' }) @JoinColumn({ name: 'id_pregunta_opcion' })
id_pregunta_opcion: PreguntaOpcion; preguntaOpcion: PreguntaOpcion;
} }
@@ -1,9 +1,13 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { RespuestaParticipanteCerradaService } from './respuesta_participante_cerrada.service'; import { RespuestaParticipanteCerradaService } from './respuesta_participante_cerrada.service';
import { RespuestaParticipanteCerradaController } from './respuesta_participante_cerrada.controller'; import { RespuestaParticipanteCerradaController } from './respuesta_participante_cerrada.controller';
import { RespuestaParticipanteCerrada } from './entities/respuesta_participante_cerrada.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({ @Module({
controllers: [RespuestaParticipanteCerradaController], imports: [TypeOrmModule.forFeature([RespuestaParticipanteCerrada])],
providers: [RespuestaParticipanteCerradaService], controllers: [RespuestaParticipanteCerradaController],
providers: [RespuestaParticipanteCerradaService],
exports:[RespuestaParticipanteCerradaService, TypeOrmModule]
}) })
export class RespuestaParticipanteCerradaModule {} export class RespuestaParticipanteCerradaModule {}
+30 -1
View File
@@ -1 +1,30 @@
export class CreateSeccionDto {} import { IsNotEmpty, IsString, IsOptional, IsArray, ValidateNested, IsInt } from 'class-validator';
import { Type } from 'class-transformer';
import { CreatePreguntaDto } from '../../pregunta/dto/create-pregunta.dto';
export class CreateSeccionDto {
@IsNotEmpty()
@IsString()
titulo: string;
@IsOptional()
@IsString()
descripcion?: string;
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => PreguntaEnSeccionDto)
preguntas?: PreguntaEnSeccionDto[];
}
export class PreguntaEnSeccionDto {
@IsNotEmpty()
@IsInt()
posicion: number;
@IsNotEmpty()
@ValidateNested()
@Type(() => CreatePreguntaDto)
pregunta: CreatePreguntaDto;
}
+3 -3
View File
@@ -16,9 +16,9 @@ export class Seccion {
@Column({ type: 'text', nullable: true }) @Column({ type: 'text', nullable: true })
titulo: string; titulo: string;
@OneToMany(() => CuestionarioSeccion,(cuestionario_seccion) => cuestionario_seccion.id_seccion ) @OneToMany(() => CuestionarioSeccion,(cuestionario_seccion) => cuestionario_seccion.seccion )
seccion:CuestionarioSeccion[]; cuestionarioSeccion:CuestionarioSeccion[];
@OneToMany(()=> SeccionPregunta, (seccion_pregunta)=> seccion_pregunta.id_seccion) @OneToMany(()=> SeccionPregunta, (seccion_pregunta)=> seccion_pregunta.seccion)
seccion_pregunta:SeccionPregunta[]; seccion_pregunta:SeccionPregunta[];
} }
+146 -14
View File
@@ -1,34 +1,166 @@
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common'; import {
Controller,
Post,
Body,
Get,
Param,
Put,
Delete,
HttpStatus,
HttpException,
Query,
} from '@nestjs/common';
import { SeccionService } from './seccion.service'; import { SeccionService } from './seccion.service';
import { CreateSeccionDto } from './dto/create-seccion.dto'; import { CreateSeccionDto, PreguntaEnSeccionDto } from './dto/create-seccion.dto';
import { UpdateSeccionDto } from './dto/update-seccion.dto'; import { UpdateSeccionDto } from './dto/update-seccion.dto';
import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiBody } from '@nestjs/swagger';
@Controller('seccion') @ApiTags('Secciones')
@Controller('secciones')
export class SeccionController { export class SeccionController {
constructor(private readonly seccionService: SeccionService) {} constructor(private readonly seccionService: SeccionService) {}
@Post() @Post()
create(@Body() createSeccionDto: CreateSeccionDto) { @ApiOperation({ summary: 'Crear una nueva sección con preguntas' })
return this.seccionService.create(createSeccionDto); @ApiBody({ type: CreateSeccionDto })
@ApiResponse({
status: HttpStatus.CREATED,
description: 'Sección creada exitosamente con sus preguntas'
})
@ApiResponse({
status: HttpStatus.BAD_REQUEST,
description: 'Datos de entrada inválidos'
})
async create(@Body() createSeccionDto: CreateSeccionDto) {
try {
return await this.seccionService.create(createSeccionDto);
} catch (error) {
throw new HttpException({
status: HttpStatus.BAD_REQUEST,
error: error.message,
}, HttpStatus.BAD_REQUEST);
}
} }
@Get() @Get()
findAll() { @ApiOperation({ summary: 'Obtener todas las secciones' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Lista de secciones obtenida exitosamente'
})
async findAll() {
return this.seccionService.findAll(); return this.seccionService.findAll();
} }
@Get(':id') @Get(':id')
findOne(@Param('id') id: string) { @ApiOperation({ summary: 'Obtener una sección específica con sus preguntas' })
return this.seccionService.findOne(+id); @ApiParam({ name: 'id', description: 'ID de la sección' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Sección encontrada con todas sus relaciones'
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Sección no encontrada'
})
async findOne(@Param('id') id: string) {
const seccion = await this.seccionService.findOneWithRelations(+id);
if (!seccion) {
throw new HttpException('Sección no encontrada', HttpStatus.NOT_FOUND);
}
return seccion;
} }
@Patch(':id') @Put(':id')
update(@Param('id') id: string, @Body() updateSeccionDto: UpdateSeccionDto) { @ApiOperation({ summary: 'Actualizar los metadatos de una sección' })
return this.seccionService.update(+id, updateSeccionDto); @ApiParam({ name: 'id', description: 'ID de la sección a actualizar' })
@ApiBody({ type: UpdateSeccionDto })
@ApiResponse({
status: HttpStatus.OK,
description: 'Sección actualizada exitosamente'
})
async update(
@Param('id') id: string,
@Body() updateSeccionDto: UpdateSeccionDto,
) {
try {
return await this.seccionService.update(+id, updateSeccionDto);
} catch (error) {
throw new HttpException({
status: HttpStatus.BAD_REQUEST,
error: error.message,
}, HttpStatus.BAD_REQUEST);
}
} }
@Delete(':id') @Delete(':id')
remove(@Param('id') id: string) { @ApiOperation({ summary: 'Eliminar una sección y sus relaciones' })
return this.seccionService.remove(+id); @ApiParam({ name: 'id', description: 'ID de la sección a eliminar' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Sección eliminada exitosamente'
})
async remove(@Param('id') id: string) {
try {
return await this.seccionService.remove(+id);
} catch (error) {
throw new HttpException({
status: HttpStatus.BAD_REQUEST,
error: error.message,
}, HttpStatus.BAD_REQUEST);
}
} }
}
@Post(':id/preguntas')
@ApiOperation({ summary: 'Agregar una nueva pregunta a la sección' })
@ApiParam({ name: 'id', description: 'ID de la sección' })
@ApiBody({ type: PreguntaEnSeccionDto })
@ApiResponse({
status: HttpStatus.CREATED,
description: 'Pregunta agregada a la sección exitosamente'
})
async agregarPregunta(
@Param('id') seccionId: string,
@Body() preguntaDto: PreguntaEnSeccionDto,
) {
try {
return await this.seccionService.agregarPreguntaExistente(
+seccionId,
preguntaDto.pregunta.id_tipo_pregunta,
preguntaDto.posicion,
);
} catch (error) {
throw new HttpException({
status: error.status || HttpStatus.BAD_REQUEST,
error: error.message,
}, error.status || HttpStatus.BAD_REQUEST);
}
}
@Delete(':seccionId/preguntas/:preguntaId')
@ApiOperation({ summary: 'Remover una pregunta de la sección' })
@ApiParam({ name: 'seccionId', description: 'ID de la sección' })
@ApiParam({ name: 'preguntaId', description: 'ID de la pregunta a remover' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Pregunta removida de la sección exitosamente'
})
async removerPregunta(
@Param('seccionId') seccionId: string,
@Param('preguntaId') preguntaId: string,
) {
try {
return await this.seccionService.eliminarPreguntaDeSeccion(
+seccionId,
+preguntaId,
);
} catch (error) {
throw new HttpException({
status: error.status || HttpStatus.BAD_REQUEST,
error: error.message,
}, error.status || HttpStatus.BAD_REQUEST);
}
}
}
+11 -2
View File
@@ -1,9 +1,18 @@
import { Module } from '@nestjs/common'; import { Module, forwardRef } from '@nestjs/common';
import { SeccionService } from './seccion.service'; import { SeccionService } from './seccion.service';
import { SeccionController } from './seccion.controller'; import { SeccionController } from './seccion.controller';
import { Seccion } from './entities/seccion.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SeccionPregunta } from '../seccion_pregunta/entities/seccion_pregunta.entity';
import { PreguntaModule } from '../pregunta/pregunta.module';
@Module({ @Module({
imports: [
TypeOrmModule.forFeature([Seccion, SeccionPregunta]),
forwardRef(() => PreguntaModule)
],
controllers: [SeccionController], controllers: [SeccionController],
providers: [SeccionService], providers: [SeccionService],
exports: [SeccionService],
}) })
export class SeccionModule {} export class SeccionModule {}
+164 -13
View File
@@ -1,26 +1,177 @@
import { Injectable } from '@nestjs/common'; import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateSeccionDto } from './dto/create-seccion.dto'; import { InjectRepository } from '@nestjs/typeorm';
import { DeleteResult, Repository } from 'typeorm';
import { Seccion } from './entities/seccion.entity';
import { PreguntaService } from '../pregunta/pregunta.service';
import { CreateSeccionDto, PreguntaEnSeccionDto } from './dto/create-seccion.dto';
import { Pregunta } from '../pregunta/entities/pregunta.entity';
import { SeccionPregunta } from 'src/seccion_pregunta/entities/seccion_pregunta.entity';
import { UpdateSeccionDto } from './dto/update-seccion.dto'; import { UpdateSeccionDto } from './dto/update-seccion.dto';
@Injectable() @Injectable()
export class SeccionService { export class SeccionService {
create(createSeccionDto: CreateSeccionDto) { constructor(
return 'This action adds a new seccion'; @InjectRepository(Seccion)
private readonly seccionRepository: Repository<Seccion>,
@InjectRepository(SeccionPregunta)
private readonly seccionPreguntaRepository: Repository<SeccionPregunta>,
private readonly preguntaService: PreguntaService,
) {}
async create(createSeccionDto: CreateSeccionDto) {
// Crear la sección base
const seccion = this.seccionRepository.create({
titulo: createSeccionDto.titulo,
descripcion: createSeccionDto.descripcion,
contador_pregunta: createSeccionDto.preguntas?.length || 0,
});
const seccionGuardada = await this.seccionRepository.save(seccion);
// Si hay preguntas, crearlas y asociarlas
if (createSeccionDto.preguntas && createSeccionDto.preguntas.length > 0) {
await this.agregarPreguntasASeccion(
seccionGuardada.id_seccion,
createSeccionDto.preguntas,
);
}
return this.findOneWithRelations(seccionGuardada.id_seccion);
} }
findAll() { private async agregarPreguntasASeccion(
return `This action returns all seccion`; seccionId: number,
preguntas: PreguntaEnSeccionDto[], // Tipo actualizado
) {
for (const { posicion, pregunta } of preguntas) {
const preguntaCreada = await this.preguntaService.create(pregunta);
const seccionPregunta = this.seccionPreguntaRepository.create({
posicion,
seccion: { id_seccion: seccionId },
pregunta: { id_pregunta: preguntaCreada?.id_pregunta },
});
await this.seccionPreguntaRepository.save(seccionPregunta);
}
} }
findOne(id: number) { async findOneWithRelations(id: number) {
return `This action returns a #${id} seccion`; return this.seccionRepository.findOne({
where: { id_seccion: id },
relations: [
'seccionPreguntas',
'seccionPreguntas.pregunta',
'seccionPreguntas.pregunta.tipoPregunta',
'seccionPreguntas.pregunta.opciones',
'seccionPreguntas.pregunta.opciones.opcion',
],
});
} }
update(id: number, updateSeccionDto: UpdateSeccionDto) { async agregarPreguntaExistente(
return `This action updates a #${id} seccion`; seccionId: number,
preguntaId: number,
posicion: number,
) {
// Verificar que existan ambos elementos
const seccion = await this.seccionRepository.findOne({
where: { id_seccion: seccionId },
});
if (!seccion) {
throw new NotFoundException(`Sección con ID ${seccionId} no encontrada`);
}
const pregunta = await this.preguntaService.findOneWithRelations(preguntaId);
if (!pregunta) {
throw new NotFoundException(`Pregunta con ID ${preguntaId} no encontrada`);
}
// Crear la relación
const seccionPregunta = this.seccionPreguntaRepository.create({
posicion,
seccion: { id_seccion: seccionId },
pregunta: { id_pregunta: preguntaId },
});
await this.seccionPreguntaRepository.save(seccionPregunta);
// Actualizar contador de preguntas
await this.seccionRepository.increment(
{ id_seccion: seccionId },
'contador_pregunta',
1,
);
return this.findOneWithRelations(seccionId);
} }
remove(id: number) { async eliminarPreguntaDeSeccion(seccionId: number, preguntaId: number) {
return `This action removes a #${id} seccion`; const relacion = await this.seccionPreguntaRepository.findOne({
where: {
seccion: { id_seccion: seccionId },
pregunta: { id_pregunta: preguntaId },
},
});
if (!relacion) {
throw new NotFoundException(
'La pregunta no está asociada a esta sección',
);
}
await this.seccionPreguntaRepository.remove(relacion);
// Actualizar contador de preguntas
await this.seccionRepository.decrement(
{ id_seccion: seccionId },
'contador_pregunta',
1,
);
return { success: true };
} }
}
async findAll(): Promise<Seccion[]> {
return this.seccionRepository.find({
relations: ['seccionPreguntas', 'seccionPreguntas.pregunta'],
});
}
async update(id: number, updateSeccionDto: UpdateSeccionDto): Promise<Seccion> {
const seccion = await this.seccionRepository.findOne({
where: { id_seccion: id }
});
if (!seccion) {
throw new NotFoundException(`Sección con ID ${id} no encontrada`);
}
if (updateSeccionDto.titulo !== undefined) {
seccion.titulo = updateSeccionDto.titulo;
}
if (updateSeccionDto.descripcion !== undefined) {
seccion.descripcion = updateSeccionDto.descripcion;
}
return this.seccionRepository.save(seccion);
}
async remove(id: number): Promise<DeleteResult> {
// Primero eliminamos las relaciones con preguntas (esto sigue siendo asíncrono)
this.seccionPreguntaRepository.delete({ seccion: { id_seccion: id } })
.catch(err => {
throw new Error(`Error al eliminar relaciones: ${err.message}`);
});
// Eliminación sincrónica usando delete (devuelve DeleteResult directamente)
const result = this.seccionRepository.delete(id);
if ((await result).affected === 0) {
throw new NotFoundException(`Sección con ID ${id} no encontrada`);
}
return result;
}
}
@@ -1,21 +1,20 @@
import { Pregunta } from 'src/pregunta/entities/pregunta.entity'; import { Pregunta } from 'src/pregunta/entities/pregunta.entity';
import { Seccion } from 'src/seccion/entities/seccion.entity'; import { Seccion } from 'src/seccion/entities/seccion.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm'; import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
@Entity()
@Entity('seccion_pregunta')
export class SeccionPregunta { export class SeccionPregunta {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id_seccion_pregunta: number; id_seccion_pregunta: number;
@Column({ type: 'tinyint' }) @Column()
posicion: number; posicion: number;
@ManyToOne(() => Pregunta, (pregunta) => pregunta.id_pregunta) @ManyToOne(() => Seccion, seccion => seccion.seccion_pregunta)
@Column({ type: 'int' }) @JoinColumn({name:'id_seccion'})
id_pregunta: number; seccion: Seccion;
@ManyToOne(() => Seccion, (seccion) => seccion.id_seccion) @ManyToOne(() => Pregunta, pregunta => pregunta.seccionesPregunta)
@Column({ type: 'int' }) @JoinColumn({name:'id_pregunta'})
id_seccion: number; pregunta: Pregunta;
} }
@@ -1,9 +1,13 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { SeccionPreguntaService } from './seccion_pregunta.service'; import { SeccionPreguntaService } from './seccion_pregunta.service';
import { SeccionPreguntaController } from './seccion_pregunta.controller'; import { SeccionPreguntaController } from './seccion_pregunta.controller';
import { SeccionPregunta } from './entities/seccion_pregunta.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([SeccionPregunta])],
controllers: [SeccionPreguntaController], controllers: [SeccionPreguntaController],
providers: [SeccionPreguntaService], providers: [SeccionPreguntaService],
exports:[SeccionPreguntaService, TypeOrmModule]
}) })
export class SeccionPreguntaModule {} export class SeccionPreguntaModule {}
@@ -1,5 +1,5 @@
import { Cuestionario } from 'src/cuestionario/entities/cuestionario.entity'; import { Cuestionario } from 'src/cuestionario/entities/cuestionario.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, ManyToOne } from 'typeorm'; import { Entity, PrimaryGeneratedColumn, Column, ManyToMany, ManyToOne, OneToMany } from 'typeorm';
@Entity('tipo_cuestionario') @Entity('tipo_cuestionario')
export class TipoCuestionario { export class TipoCuestionario {
@@ -9,6 +9,6 @@ export class TipoCuestionario {
@Column({ type: 'varchar', length: 30 }) @Column({ type: 'varchar', length: 30 })
tipo_cuestionario: string; tipo_cuestionario: string;
@ManyToOne(()=> Cuestionario,(cuestionario)=> cuestionario.id_tipo_cuestionario) @OneToMany(()=> Cuestionario,(cuestionario)=> cuestionario.tipo_cuestionario)
cuestionario:Cuestionario[]; cuestionarios:Cuestionario[];
} }
@@ -1,9 +1,13 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TipoCuestionarioService } from './tipo_cuestionario.service'; import { TipoCuestionarioService } from './tipo_cuestionario.service';
import { TipoCuestionarioController } from './tipo_cuestionario.controller'; import { TipoCuestionarioController } from './tipo_cuestionario.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TipoCuestionario } from './entities/tipo_cuestionario.entity';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([TipoCuestionario])],
controllers: [TipoCuestionarioController], controllers: [TipoCuestionarioController],
providers: [TipoCuestionarioService], providers: [TipoCuestionarioService],
exports:[TipoCuestionarioService, TypeOrmModule]
}) })
export class TipoCuestionarioModule {} export class TipoCuestionarioModule {}
@@ -1 +1,9 @@
export class CreateTipoPreguntaDto {} import { IsNotEmpty, IsString, MaxLength, MinLength } from 'class-validator';
export class CreateTipoPreguntaDto {
@IsNotEmpty()
@IsString()
@MinLength(3)
@MaxLength(50)
tipo_pregunta: string;
}
@@ -9,6 +9,6 @@ export class TipoPregunta {
@Column() @Column()
tipo_pregunta: string; tipo_pregunta: string;
@OneToMany(() => Pregunta, pregunta => pregunta.id_tipo_pregunta) @OneToMany(() => Pregunta, pregunta => pregunta.tipo_pregunta)
preguntas: Pregunta[]; preguntas: Pregunta[];
} }
@@ -0,0 +1,32 @@
import { Controller, Post, Body, HttpStatus, HttpException } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiBody } from '@nestjs/swagger';
import { CreateTipoPreguntaDto } from './dto/create-tipo_pregunta.dto';
import { TipoPreguntaService } from './tipo_pregunta.service';
@ApiTags('Tipos de Pregunta')
@Controller('tipos-pregunta')
export class TipoPreguntaController {
constructor(private readonly tipoPreguntaService: TipoPreguntaService) {}
@Post()
@ApiOperation({ summary: 'Crear un nuevo tipo de pregunta' })
@ApiBody({ type: CreateTipoPreguntaDto })
@ApiResponse({
status: 201,
description: 'Tipo de pregunta creado exitosamente'
})
@ApiResponse({
status: 409,
description: 'El tipo de pregunta ya existe'
})
async create(@Body() createTipoPreguntaDto: CreateTipoPreguntaDto) {
try {
return await this.tipoPreguntaService.create(createTipoPreguntaDto);
} catch (error) {
throw new HttpException({
status: error.status || HttpStatus.BAD_REQUEST,
error: error.message,
}, error.status || HttpStatus.BAD_REQUEST);
}
}
}
+9 -2
View File
@@ -1,9 +1,16 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TipoPreguntaService } from './tipo_pregunta.service'; import { TypeOrmModule } from '@nestjs/typeorm';
import { TipoPregunta } from './entities/tipo_pregunta.entity';
import { TipoPreguntaController } from './tipo_pregunta.controller'; import { TipoPreguntaController } from './tipo_pregunta.controller';
import { TipoPreguntaService } from './tipo_pregunta.service';
@Module({ @Module({
imports: [
TypeOrmModule.forFeature([TipoPregunta]),
],
controllers: [TipoPreguntaController], controllers: [TipoPreguntaController],
providers: [TipoPreguntaService], providers: [TipoPreguntaService],
exports: [TipoPreguntaService, TypeOrmModule],
}) })
export class TipoPreguntaModule {} export class TipoPreguntaModule {}
@@ -0,0 +1,34 @@
import { Injectable, ConflictException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TipoPregunta } from './entities/tipo_pregunta.entity';
import { CreateTipoPreguntaDto } from './dto/create-tipo_pregunta.dto';
@Injectable()
export class TipoPreguntaService {
constructor(
@InjectRepository(TipoPregunta)
private readonly tipoPreguntaRepository: Repository<TipoPregunta>,
) {}
async create(createTipoPreguntaDto: CreateTipoPreguntaDto): Promise<TipoPregunta> {
// Verificar si ya existe un tipo con el mismo nombre
const tipoExistente = await this.tipoPreguntaRepository.findOne({
where: { tipo_pregunta: createTipoPreguntaDto.tipo_pregunta },
});
if (tipoExistente) {
throw new ConflictException(
`El tipo de pregunta '${createTipoPreguntaDto.tipo_pregunta}' ya existe`,
);
}
// Crear y guardar el nuevo tipo
const nuevoTipo = this.tipoPreguntaRepository.create({
tipo_pregunta: createTipoPreguntaDto.tipo_pregunta,
});
return await this.tipoPreguntaRepository.save(nuevoTipo);
}
}