diff --git a/.gitignore b/.gitignore index aff1adf..8dabeec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # archivo env .env +/upload/* + # compiled output /dist /node_modules diff --git a/package-lock.json b/package-lock.json index 792ebbd..739fbe1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "bcrypt": "^5.0.1", "class-transformer": "^0.5.1", "class-validator": "^0.13.2", + "csvtojson": "^2.0.10", "moment": "^2.29.3", "mysql2": "^2.3.3", "nodemailer": "^6.7.3", @@ -2926,6 +2927,11 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, "node_modules/body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", @@ -3618,6 +3624,33 @@ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", "dev": true }, + "node_modules/csvtojson": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/csvtojson/-/csvtojson-2.0.10.tgz", + "integrity": "sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ==", + "dependencies": { + "bluebird": "^3.5.1", + "lodash": "^4.17.3", + "strip-bom": "^2.0.0" + }, + "bin": { + "csvtojson": "bin/csvtojson" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/csvtojson/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -5402,6 +5435,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, "node_modules/isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -12015,6 +12053,11 @@ } } }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, "body-parser": { "version": "1.19.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.2.tgz", @@ -12554,6 +12597,26 @@ } } }, + "csvtojson": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/csvtojson/-/csvtojson-2.0.10.tgz", + "integrity": "sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ==", + "requires": { + "bluebird": "^3.5.1", + "lodash": "^4.17.3", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, "data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -13884,6 +13947,11 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=" + }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", diff --git a/package.json b/package.json index 7efe846..147fed8 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "bcrypt": "^5.0.1", "class-transformer": "^0.5.1", "class-validator": "^0.13.2", + "csvtojson": "^2.0.10", "moment": "^2.29.3", "mysql2": "^2.3.3", "nodemailer": "^6.7.3", diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index 11ce4fa..336f12b 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -23,8 +23,8 @@ export class AuthService { validate() {} loginUsuario(usuario: string, password: string) { - return this.usuarioService.findByUsuario(usuario).then((usuario) => { - if (!this.bcryptService.comparar(password, usuario.password)) + return this.usuarioService.findByUsuario(usuario, false).then((usuario) => { + if (!usuario || !this.bcryptService.comparar(password, usuario.password)) throw new UnauthorizedException( 'Usuario y/o password incorrectos, trata de nuevo.', ); diff --git a/src/carrera/carrera.module.ts b/src/carrera/carrera.module.ts index 5707bd3..f64b841 100644 --- a/src/carrera/carrera.module.ts +++ b/src/carrera/carrera.module.ts @@ -3,9 +3,10 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { CarreraController } from './carrera.controller'; import { CarreraService } from './carrera.service'; import { Carrera } from './entity/carrera.entity'; +import { InstitucionModule } from '../institucion/institucion.module'; @Module({ - imports: [TypeOrmModule.forFeature([Carrera])], + imports: [InstitucionModule, TypeOrmModule.forFeature([Carrera])], controllers: [CarreraController], providers: [CarreraService], exports: [CarreraService], diff --git a/src/carrera/carrera.service.ts b/src/carrera/carrera.service.ts index 7d36466..b421170 100644 --- a/src/carrera/carrera.service.ts +++ b/src/carrera/carrera.service.ts @@ -2,13 +2,23 @@ import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Carrera } from './entity/carrera.entity'; +import { InstitucionService } from '../institucion/institucion.service'; @Injectable() export class CarreraService { constructor( @InjectRepository(Carrera) private repository: Repository, + private institucionService: InstitucionService, ) {} + create(id_institucion: number, carrera: string) { + return this.institucionService + .findById(id_institucion) + .then((institucion) => + this.repository.save(this.repository.create({ carrera, institucion })), + ); + } + findAll() { return this.repository.find(); } @@ -19,4 +29,12 @@ export class CarreraService { return carrera; }); } + + findByCarrera(carrera: string, noExisteValidar = true) { + return this.repository.findOne({ carrera }).then((carrera) => { + if (noExisteValidar && !carrera) + throw new NotFoundException('No existe esta carrera.'); + return carrera; + }); + } } diff --git a/src/modulo/modulo.service.ts b/src/modulo/modulo.service.ts index 7761798..0c2b0cc 100644 --- a/src/modulo/modulo.service.ts +++ b/src/modulo/modulo.service.ts @@ -17,10 +17,6 @@ export class ModuloService { async create(id_institucion: number, modulo: string) { const institucion = await this.institucionService.findById(id_institucion); - const nuevoModulo = this.repository.create({ - institucion, - modulo, - }); return this.repository .findOne({ modulo, institucion }) @@ -29,7 +25,12 @@ export class ModuloService { throw new ConflictException( 'Ya existe un módulo con este nombre, intente con otro nombre.', ); - return this.repository.save(nuevoModulo); + return this.repository.save( + this.repository.create({ + institucion, + modulo, + }), + ); }) .then((_) => ({ message: 'Se creo correctamente el módulo.' })); } diff --git a/src/tipo-usuario/tipo-usuario.service.ts b/src/tipo-usuario/tipo-usuario.service.ts index 6fec3d5..f384adf 100644 --- a/src/tipo-usuario/tipo-usuario.service.ts +++ b/src/tipo-usuario/tipo-usuario.service.ts @@ -35,4 +35,12 @@ export class TipoUsuarioService { return tipoUsuario; }); } + + findByTipoUsuario(tipo_usuario: string, noExisteValidar = true) { + return this.repository.findOne({ tipo_usuario }).then((tipoUsuario) => { + if (noExisteValidar && !tipoUsuario) + throw new NotFoundException('No existe este tipo usuairo'); + return tipoUsuario; + }); + } } diff --git a/src/upload-file/dto/upload-file-carga-masiva-usuario.dto.ts b/src/upload-file/dto/upload-file-carga-masiva-usuario.dto.ts new file mode 100644 index 0000000..f9b3e2c --- /dev/null +++ b/src/upload-file/dto/upload-file-carga-masiva-usuario.dto.ts @@ -0,0 +1,13 @@ +export class UploadFileCargaMasivaUsuarioDto { + carrera?: string; + + numero_cuenta?: string; + + tipo_usuario?: string; + + nombres: string; + + apellido_p: string; + + apellido_m: string; +} diff --git a/src/upload-file/upload-file.controller.ts b/src/upload-file/upload-file.controller.ts index 9a0fe52..e2ab57c 100644 --- a/src/upload-file/upload-file.controller.ts +++ b/src/upload-file/upload-file.controller.ts @@ -16,21 +16,27 @@ export class UploadFileController { constructor(private uploadFileService: UploadFileService) {} @Post('carga-masiva-equipos') - @UseInterceptors(FileInterceptor('file')) + @UseInterceptors(FileInterceptor('csv')) cargaMasivaEquipos( @UploadedFile() file: Express.Multer.File, @Query() query: IdInstitucionDto, ) { - this.uploadFileService.createEquipos(file, Number(query.id_institucion)); + return this.uploadFileService.createEquipos( + file, + Number(query.id_institucion), + ); } @Post('carga-masiva-usuarios') - @UseInterceptors(FileInterceptor('file')) + @UseInterceptors(FileInterceptor('csv')) cargaMasivaUsuarios( @UploadedFile() file: Express.Multer.File, @Query() query: IdInstitucionDto, ) { - this.uploadFileService.createEquipos(file, Number(query.id_institucion)); + return this.uploadFileService.createUsuarios( + file, + Number(query.id_institucion), + ); } @Get('download-logo') @@ -40,12 +46,12 @@ export class UploadFileController { @Get('download-plantilla-equipos') downloadPlantillaEquipos(@Response() res, @Query() query: IdInstitucionDto) { - return res.download("./upload/plantilla_equipos.csv") + return res.download('./upload/plantilla_equipos.csv'); } @Get('download-plantilla-usuarios') downloadPlantillaUsuarios(@Response() res, @Query() query: IdInstitucionDto) { - return res.download("./upload/plantilla_alumnos.csv") + return res.download('./upload/plantilla_alumnos.csv'); } @Post('upload-logo') diff --git a/src/upload-file/upload-file.module.ts b/src/upload-file/upload-file.module.ts index 7b0bf0a..34ec06e 100644 --- a/src/upload-file/upload-file.module.ts +++ b/src/upload-file/upload-file.module.ts @@ -2,11 +2,17 @@ import { ConflictException, Module } from '@nestjs/common'; import { MulterModule } from '@nestjs/platform-express'; import { UploadFileController } from './upload-file.controller'; import { UploadFileService } from './upload-file.service'; +import { CarreraModule } from '../carrera/carrera.module'; import { EquipoModule } from '../equipo/equipo.module'; +import { InstitucionModule } from '../institucion/institucion.module'; +import { TipoUsuarioModule } from '../tipo-usuario/tipo-usuario.module'; import { UsuarioModule } from '../usuario/usuario.module'; @Module({ imports: [ + CarreraModule, + EquipoModule, + InstitucionModule, MulterModule.register({ dest: './upload', fileFilter: (req, file, cb) => { @@ -18,7 +24,7 @@ import { UsuarioModule } from '../usuario/usuario.module'; return cb(null, true); }, }), - EquipoModule, + TipoUsuarioModule, UsuarioModule, ], controllers: [UploadFileController], diff --git a/src/upload-file/upload-file.service.ts b/src/upload-file/upload-file.service.ts index d2f651a..9bf8b1f 100644 --- a/src/upload-file/upload-file.service.ts +++ b/src/upload-file/upload-file.service.ts @@ -1,19 +1,140 @@ -import { Injectable } from '@nestjs/common'; +import * as fs from 'fs'; +import * as csvtojson from 'csvtojson'; +import { BadRequestException, Injectable } from '@nestjs/common'; +import { CarreraService } from '../carrera/carrera.service'; import { EquipoService } from '../equipo/equipo.service'; +import { InstitucionService } from '../institucion/institucion.service'; +import { TipoUsuarioService } from '../tipo-usuario/tipo-usuario.service'; import { UsuarioService } from '../usuario/usuario.service'; +import { Carrera } from 'src/carrera/entity/carrera.entity'; +import { Institucion } from 'src/institucion/entity/institucion.entity'; +import { TipoUsuario } from 'src/tipo-usuario/entity/tipo-usuario.entity'; +import { UploadFileCargaMasivaUsuarioDto } from './dto/upload-file-carga-masiva-usuario.dto'; @Injectable() export class UploadFileService { constructor( + private carreraService: CarreraService, private equipoService: EquipoService, + private institucionService: InstitucionService, + private tipoUsuarioService: TipoUsuarioService, private usuarioService: UsuarioService, ) {} - createEquipos(file: Express.Multer.File, id_institucion: number) {} + async createEquipos(file: Express.Multer.File, id_institucion: number) { + const path = `${file.destination}/${file.filename}`; + const institucion = await this.institucionService.findById(id_institucion); - createUsuarios(file: Express.Multer.File, id_institucion: number) {} + if (!file) throw new BadRequestException('No se mando un archivo.'); + csvtojson() + .fromFile(path) + .then(async (equipos) => { + for (let i = 0; i < equipos.length; i++) {} + }); + } + + async createUsuarios(file: Express.Multer.File, id_institucion: number) { + const path = `${file.destination}/${file.filename}`; + const institucion = await this.institucionService.findById(id_institucion); + const errores: string[] = []; + + if (!file) throw new BadRequestException('No se mando un archivo.'); + return csvtojson() + .fromFile(path) + .then(async (usuarios: [UploadFileCargaMasivaUsuarioDto]) => { + for (let i = 0; i < usuarios.length; i++) { + if (!usuarios[i].numero_cuenta || !usuarios[i].tipo_usuario) { + if (!usuarios[i].numero_cuenta) + errores.push( + `${this.errorBase(i)} falta del campo numero_cuenta.`, + ); + if (!usuarios[i].tipo_usuario) + errores.push( + `${this.errorBase(i)} falta del campo tipo_usuario.`, + ); + if (!usuarios[i].nombres) + errores.push(`${this.errorBase(i)} falta del campo nombres.`); + if (!usuarios[i].apellido_m) + errores.push(`${this.errorBase(i)} falta del campo apellido_m.`); + if (!usuarios[i].apellido_p) + errores.push(`${this.errorBase(i)} falta del campo apellido_p.`); + continue; + } + + const tipoUsuario = await this.tipoUsuarioService.findByTipoUsuario( + usuarios[i].tipo_usuario, + false, + ); + const carrera = + usuarios[i].carrera && tipoUsuario.id_tipo_usuario !== 5 + ? await this.carreraService + .findByCarrera(usuarios[i].carrera, false) + .then((carrera) => { + if (!carrera) + return this.carreraService.create( + institucion.id_institucion, + usuarios[i].carrera, + ); + return carrera; + }) + : null; + const usuario = await this.usuarioService.findByUsuario( + usuarios[i].numero_cuenta, + false, + ); + + console.log('hola'); + console.log(usuario); + if (!tipoUsuario || (tipoUsuario.id_tipo_usuario !== 5 && !carrera)) { + if (tipoUsuario.id_tipo_usuario !== 5 && !carrera) + errores.push( + `${this.errorBase(i)} falta del campo carrera para el alumno.`, + ); + if (!tipoUsuario) + errores.push( + `${this.errorBase(i)} no existe este tipo de usuario.`, + ); + continue; + } + if (usuario) { + const data: { + carrera?: Carrera; + id_usuario: number; + institucion: Institucion; + tipoUsuario: TipoUsuario; + } = { + id_usuario: usuario.id_usuario, + institucion, + tipoUsuario, + }; + + if (carrera) data.carrera = carrera; + await this.usuarioService.update(data).then((usuarioUpdated) => {}); + } else + await this.usuarioService + .create( + usuarios[i].numero_cuenta, + institucion.id_institucion, + tipoUsuario.id_tipo_usuario, + carrera ? carrera.id_carrera : null, + ) + .then((usuario) => {}); + } + return fs.unlink(path, (err) => { + console.log(err); + }); + }) + .then((res) => { + console.log(res); + return { message: 'Se subió y cargo correctamente tu archivo csv.' }; + }); + } downloadLogo(id_institucion: number) {} + errorBase(index) { + return `Se salto la linea ${index + 2} por:`; + } + uploadLogo(file: Express.Multer.File, id_institucion: number) {} } diff --git a/src/usuario/entity/usuario.entity.ts b/src/usuario/entity/usuario.entity.ts index 228588f..b075d52 100644 --- a/src/usuario/entity/usuario.entity.ts +++ b/src/usuario/entity/usuario.entity.ts @@ -22,10 +22,13 @@ export class Usuario { @Column({ type: Boolean, nullable: false, default: false }) multa: boolean; - @Column({ type: String, nullable: true, length: 60 }) + @Column({ type: String, nullable: false, length: 100 }) + nombre: string; + + @Column({ type: String, nullable: true, length: 60, default: null }) password: string; - @Column({ type: String, nullable: true, length: 10 }) + @Column({ type: String, nullable: true, length: 10, default: null }) telefono: string; @Column({ type: String, nullable: false, length: 10 }) diff --git a/src/usuario/usuario.service.ts b/src/usuario/usuario.service.ts index 1a54654..5b5bc5d 100644 --- a/src/usuario/usuario.service.ts +++ b/src/usuario/usuario.service.ts @@ -26,6 +26,35 @@ export class UsuarioService { private tipoUsuarioService: TipoUsuarioService, ) {} + async create( + usuario: string, + id_institucion: number, + id_tipo_usuario: number, + id_carrera?: number, + ) { + const carrera = id_carrera + ? await this.carreraService.findById(id_carrera) + : null; + const institucion = await this.institucionService.findById(id_institucion); + const tipoUsuario = await this.tipoUsuarioService.findById(id_tipo_usuario); + const data: { + carrera?: Carrera; + institucion: Institucion; + tipoUsuario: TipoUsuario; + } = { institucion, tipoUsuario }; + + if (carrera) data.carrera = carrera; + return this.findByUsuario(usuario, false) + .then((existeUsuario) => { + if (existeUsuario) + throw new ConflictException('Ya existe este usuario.'); + return this.repository.save(this.repository.create(data)); + }) + .then((usuario) => ({ + message: `Se creo correctamente el usuairo ${usuario.usuario}`, + })); + } + correoPassword(password: string) { return `

Estimado usuario, su registro se realizó con éxito.

Para acceder al servicio debe hacerlo con los siguientes datos:

@@ -80,16 +109,18 @@ export class UsuarioService { return this.repository.find(busqueda); } - findById(id_usuario: number) { + findById(id_usuario: number, noExisteValidar = true, password = false) { return this.repository.findOne({ id_usuario }).then((usuario) => { - if (!usuario) throw new NotFoundException('No existe este usuario.'); + if (noExisteValidar && (!usuario || (password && !usuario.password))) + throw new NotFoundException('No existe este usuario.'); return usuario; }); } - findByUsuario(usuario: string) { + findByUsuario(usuario: string, noExisteValidar = true, password = false) { return this.repository.findOne({ usuario }).then((usuario) => { - if (!usuario) throw new NotFoundException('No existe este usuario.'); + if (noExisteValidar && (!usuario || (password && !usuario.password))) + throw new NotFoundException('No existe este usuario.'); return usuario; }); } @@ -109,10 +140,8 @@ export class UsuarioService { passwordReset(id_usuario: number) { const password = this.generarPassword(); - return this.findById(id_usuario) + return this.findById(id_usuario, true, true) .then((usuario) => { - if (!usuario.password) - throw new ConflictException('No existe este usuario.'); usuario.password = this.bcryptService.encriptar(password); return this.repository.save(usuario); }) @@ -152,10 +181,8 @@ export class UsuarioService { } update(attrs: Partial) { - return this.findById(attrs.id_usuario) + return this.findById(attrs.id_usuario, true, true) .then((usuario) => { - if (!usuario.password) - throw new ConflictException('No existe este usuario.'); Object.assign(usuario, attrs); return this.repository.save(usuario); }) diff --git a/upload/plantilla_alumnos.csv b/upload/plantilla_alumnos.csv index 685aa3f..ba81afd 100644 --- a/upload/plantilla_alumnos.csv +++ b/upload/plantilla_alumnos.csv @@ -1,4 +1,4 @@ -numero_cuenta,nombre,apellido_p,apellido_m,carrra,tipo_usuario +numero_cuenta,nombres,apellido_p,apellido_m,carrra,tipo_usuario 313144567,Nombre 1,Apellido_p 1,Apellido_m 1,Carrera,Alumno 415895839,Nombre 2,Apellido_p 2,Apellido_m 2,Posgrado,Posgrado 813573,Nombre 3,Apellido_p 3,Apellido_m 3,,Profesor \ No newline at end of file