[docx] Start MECANICA BASICA generation
This commit is contained in:
parent
74f8009083
commit
c46fbd3c26
BIN
img/fondo_certificado_mecanica_basica.jpg
Normal file
BIN
img/fondo_certificado_mecanica_basica.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 709 KiB |
38
src/certs/CertData.ts
Normal file
38
src/certs/CertData.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
export type CertData<T> = {
|
||||||
|
matpel: T,
|
||||||
|
/**
|
||||||
|
* Full name in format: "FIRST_NAMES LAST_NAME_1 LAST_NAME_2"
|
||||||
|
*/
|
||||||
|
personFullName: string,
|
||||||
|
/**
|
||||||
|
* DNI as a string.
|
||||||
|
*
|
||||||
|
* E.g.: "04123523"
|
||||||
|
*/
|
||||||
|
personDni: string,
|
||||||
|
/**
|
||||||
|
* Cert code as a string. Must have length = 4.
|
||||||
|
*
|
||||||
|
* E.g.: "0322"
|
||||||
|
*/
|
||||||
|
certCode: string,
|
||||||
|
/**
|
||||||
|
* Year of emission as a string.
|
||||||
|
*
|
||||||
|
* E.g.: "2023"
|
||||||
|
*/
|
||||||
|
certYear: string,
|
||||||
|
/**
|
||||||
|
* Month of emission.
|
||||||
|
*
|
||||||
|
* E.g.: "05"
|
||||||
|
*/
|
||||||
|
certMonth: string,
|
||||||
|
/**
|
||||||
|
* Day of emission.
|
||||||
|
*
|
||||||
|
* E.g.: "23"
|
||||||
|
*/
|
||||||
|
certDay: string,
|
||||||
|
}
|
@ -2,13 +2,12 @@ import {
|
|||||||
Document, Packer, Paragraph, PageOrientation, ImageRun,
|
Document, Packer, Paragraph, PageOrientation, ImageRun,
|
||||||
HorizontalPositionRelativeFrom, VerticalPositionRelativeFrom,
|
HorizontalPositionRelativeFrom, VerticalPositionRelativeFrom,
|
||||||
FrameAnchorType,
|
FrameAnchorType,
|
||||||
HorizontalPositionAlign,
|
|
||||||
VerticalPositionAlign,
|
|
||||||
TextRun,
|
TextRun,
|
||||||
AlignmentType,
|
AlignmentType,
|
||||||
} from "docx";
|
} from "docx";
|
||||||
import * as QR from "qrcode";
|
import * as QR from "qrcode";
|
||||||
import { Matpel, cm, cmText, cmToEmu, getImage, getMatpelHours, getMatpelLabel } from "./utils";
|
import { Matpel, cm, cmText, cmToEmu, getImage, getMatpelHours, getMatpelLabel } from "./utils";
|
||||||
|
import { CertData } from "./CertData";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -104,50 +103,13 @@ const imgEATE = getImage({
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type CertData = {
|
|
||||||
matpel: Matpel,
|
|
||||||
/**
|
|
||||||
* Full name in format: "FIRST_NAMES LAST_NAME_1 LAST_NAME_2"
|
|
||||||
*/
|
|
||||||
personFullName: string,
|
|
||||||
/**
|
|
||||||
* DNI as a string.
|
|
||||||
*
|
|
||||||
* E.g.: "04123523"
|
|
||||||
*/
|
|
||||||
personDni: string,
|
|
||||||
/**
|
|
||||||
* Cert code as a string. Must have length = 4.
|
|
||||||
*
|
|
||||||
* E.g.: "0322"
|
|
||||||
*/
|
|
||||||
certCode: string,
|
|
||||||
/**
|
|
||||||
* Year of emission as a string.
|
|
||||||
*
|
|
||||||
* E.g.: "2023"
|
|
||||||
*/
|
|
||||||
certYear: string,
|
|
||||||
/**
|
|
||||||
* Month of emission.
|
|
||||||
*
|
|
||||||
* E.g.: "05"
|
|
||||||
*/
|
|
||||||
certMonth: string,
|
|
||||||
/**
|
|
||||||
* Day of emission.
|
|
||||||
*
|
|
||||||
* E.g.: "23"
|
|
||||||
*/
|
|
||||||
certDay: string,
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Generates a certificate in format DOCX and in-place.
|
* Generates a certificate in format DOCX and in-place.
|
||||||
*
|
*
|
||||||
* @param props Data to use in the certificate
|
* @param props Data to use in the certificate
|
||||||
* @returns A buffer of the DOCX document
|
* @returns A buffer of the DOCX document
|
||||||
*/
|
*/
|
||||||
export async function matpelCert(props: CertData): Promise<Buffer> {
|
export async function matpelCert(props: CertData<Matpel>): Promise<Buffer> {
|
||||||
const certificateName = new Paragraph({
|
const certificateName = new Paragraph({
|
||||||
frame: {
|
frame: {
|
||||||
position: {
|
position: {
|
||||||
|
167
src/certs/MECANICA_BASICA.ts
Normal file
167
src/certs/MECANICA_BASICA.ts
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
import {
|
||||||
|
Document, Packer, Paragraph, PageOrientation, ImageRun,
|
||||||
|
HorizontalPositionRelativeFrom, VerticalPositionRelativeFrom,
|
||||||
|
FrameAnchorType,
|
||||||
|
TextRun,
|
||||||
|
AlignmentType,
|
||||||
|
} from "docx";
|
||||||
|
import * as QR from "qrcode";
|
||||||
|
import { Matpel, cm, cmText, cmToEmu, createSimpleText, getImage, getMatpelHours, getMatpelLabel, getQR } from "./utils";
|
||||||
|
import { CertData } from "./CertData";
|
||||||
|
|
||||||
|
const imgFondoDoc = getImage({
|
||||||
|
name: "fondo_certificado_mecanica_basica.jpg",
|
||||||
|
height: 21.23,
|
||||||
|
width: 29.8,
|
||||||
|
horizontalOffset: 0,
|
||||||
|
verticalOffset: 0,
|
||||||
|
behindDocument: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const imgCIP = getImage({
|
||||||
|
name: "colegio_ingenieros_logo.png",
|
||||||
|
height: 2.15,
|
||||||
|
width: 2.15,
|
||||||
|
horizontalOffset: 0.22,
|
||||||
|
verticalOffset: 16.45,
|
||||||
|
});
|
||||||
|
|
||||||
|
const imgMichigan = getImage({
|
||||||
|
name: "michigan_logo.png",
|
||||||
|
height: 1.96,
|
||||||
|
width: 2.22,
|
||||||
|
horizontalOffset: 0.31,
|
||||||
|
verticalOffset: 18.86,
|
||||||
|
});
|
||||||
|
|
||||||
|
const tCertificate = createSimpleText({
|
||||||
|
xPosition: 7.75,
|
||||||
|
yPosition: 1.64,
|
||||||
|
width: 9,
|
||||||
|
height: 1.56,
|
||||||
|
text: "CERTIFICADO",
|
||||||
|
size: 60,
|
||||||
|
bold: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Se expide el presente certificado a:
|
||||||
|
const tExpediteText = createSimpleText({
|
||||||
|
xPosition: 0.76,
|
||||||
|
yPosition: 3.45,
|
||||||
|
width: 7.88,
|
||||||
|
height: 1,
|
||||||
|
text: "Se expide el presente certificado a:",
|
||||||
|
size: 22,
|
||||||
|
});
|
||||||
|
|
||||||
|
// MECANICA BASICA
|
||||||
|
const tCourse = createSimpleText({
|
||||||
|
xPosition: 1.43,
|
||||||
|
yPosition: 7,
|
||||||
|
width: 20.08,
|
||||||
|
height: 1.5,
|
||||||
|
text: "MECÁNICA BÁSICA",
|
||||||
|
size: 72,
|
||||||
|
bold: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// En los temas de...
|
||||||
|
const tTopics = createSimpleText({
|
||||||
|
xPosition: 1.43,
|
||||||
|
yPosition: 8.75,
|
||||||
|
width: 20.08,
|
||||||
|
height: 1.5,
|
||||||
|
text: "En los temas de: Lineamientos del Ministerio de Transportes y Comunicaciones (MTC), Operatividad efectiva del equipo. Procedimiento de operación correcta. Equipamiento y mantenimiento preventivo, Medidas de Control y Tipos de IPERC, Cuidados de la maquina durante la operación, Uso de pedales y controles, Funcionamiento del motor",
|
||||||
|
size: 22,
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function mecanicaBasicaCert(props: CertData<null>): Promise<Buffer> {
|
||||||
|
const imgQR = await getQR({
|
||||||
|
dni: props.personDni,
|
||||||
|
height: 2.2,
|
||||||
|
width: 2.2,
|
||||||
|
horizontalOffset: 24.63,
|
||||||
|
verticalOffset: 17.01,
|
||||||
|
});
|
||||||
|
|
||||||
|
// FERNANDO ARAOZ
|
||||||
|
const tName = createSimpleText({
|
||||||
|
xPosition: 0.38,
|
||||||
|
yPosition: 4.35,
|
||||||
|
width: 24.08,
|
||||||
|
height: 1.51,
|
||||||
|
text: props.personFullName,
|
||||||
|
size: 52,
|
||||||
|
bold: true,
|
||||||
|
underline: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fecha de Emision: ...
|
||||||
|
const tContentPart1 = new Paragraph({
|
||||||
|
frame: {
|
||||||
|
position: {
|
||||||
|
x: cmText(1.43),
|
||||||
|
y: cmText(6.16),
|
||||||
|
},
|
||||||
|
width: cmText(20.08),
|
||||||
|
height: cmText(1),
|
||||||
|
anchor: {
|
||||||
|
horizontal: FrameAnchorType.MARGIN,
|
||||||
|
vertical: FrameAnchorType.MARGIN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: "Identificado con DNI N° ",
|
||||||
|
font: "Arial",
|
||||||
|
size: 22,
|
||||||
|
}),
|
||||||
|
new TextRun({
|
||||||
|
text: props.personDni,
|
||||||
|
font: "Arial",
|
||||||
|
size: 22,
|
||||||
|
bold: true,
|
||||||
|
}),
|
||||||
|
new TextRun({
|
||||||
|
text: ", al haber aprobado el curso de capacitación en:",
|
||||||
|
font: "Arial",
|
||||||
|
size: 22,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
alignment: AlignmentType.LEFT,
|
||||||
|
});
|
||||||
|
|
||||||
|
const doc = new Document({
|
||||||
|
sections: [
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
page: {
|
||||||
|
size: {
|
||||||
|
orientation: PageOrientation.LANDSCAPE,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
tCertificate,
|
||||||
|
tExpediteText,
|
||||||
|
tName,
|
||||||
|
tContentPart1,
|
||||||
|
tCourse,
|
||||||
|
tTopics,
|
||||||
|
new Paragraph({
|
||||||
|
children: [
|
||||||
|
imgFondoDoc,
|
||||||
|
imgCIP,
|
||||||
|
imgMichigan,
|
||||||
|
imgQR,
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return document as a buffer
|
||||||
|
return await Packer.toBuffer(doc);
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import { HorizontalPositionRelativeFrom, ImageRun, VerticalPositionRelativeFrom, convertMillimetersToTwip } from "docx";
|
import { AlignmentType, FrameAnchorType, HorizontalPositionRelativeFrom, ImageRun, Paragraph, TextRun, VerticalPositionRelativeFrom, convertMillimetersToTwip } from "docx";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
|
import * as QR from "qrcode";
|
||||||
|
|
||||||
|
|
||||||
// To define centimeters in a image's size
|
// To define centimeters in a image's size
|
||||||
@ -50,6 +51,77 @@ export function getImage(data: ImgConfig): ImageRun {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getQR(data : {
|
||||||
|
dni: string,
|
||||||
|
height: number,
|
||||||
|
width: number,
|
||||||
|
horizontalOffset: number,
|
||||||
|
verticalOffset: number,
|
||||||
|
behindDocument?: boolean,
|
||||||
|
}): Promise<ImageRun> {
|
||||||
|
const qr = await QR.toDataURL(`https://www.eegsac.com/alumnoscertificados.php?DNI=${data.dni}`, {margin: 1});
|
||||||
|
|
||||||
|
return new ImageRun({
|
||||||
|
// Magic path, based on dist folder
|
||||||
|
data: qr,
|
||||||
|
transformation: {
|
||||||
|
height: cm(data.height),
|
||||||
|
width: cm(data.width),
|
||||||
|
},
|
||||||
|
floating: {
|
||||||
|
zIndex: 0,
|
||||||
|
horizontalPosition: {
|
||||||
|
relative: HorizontalPositionRelativeFrom.LEFT_MARGIN,
|
||||||
|
offset: cmToEmu(data.horizontalOffset),
|
||||||
|
},
|
||||||
|
verticalPosition: {
|
||||||
|
relative: VerticalPositionRelativeFrom.TOP_MARGIN,
|
||||||
|
offset: cmToEmu(data.verticalOffset),
|
||||||
|
},
|
||||||
|
behindDocument: data.behindDocument ?? false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createSimpleText(data: {
|
||||||
|
xPosition: number,
|
||||||
|
yPosition: number,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
text: string,
|
||||||
|
font?: string,
|
||||||
|
bold?: boolean,
|
||||||
|
size: number,
|
||||||
|
underline?: Record<string, never>,
|
||||||
|
alignment?: AlignmentType,
|
||||||
|
}): Paragraph {
|
||||||
|
|
||||||
|
return new Paragraph({
|
||||||
|
frame: {
|
||||||
|
position: {
|
||||||
|
x: cmText(data.xPosition),
|
||||||
|
y: cmText(data.yPosition),
|
||||||
|
},
|
||||||
|
width: cmText(data.width),
|
||||||
|
height: cmText(data.height),
|
||||||
|
anchor: {
|
||||||
|
horizontal: FrameAnchorType.MARGIN,
|
||||||
|
vertical: FrameAnchorType.MARGIN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: data.text,
|
||||||
|
bold: data.bold,
|
||||||
|
font: data.font ?? "Arial",
|
||||||
|
size: data.size,
|
||||||
|
underline: data.underline,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
alignment: data.alignment ?? AlignmentType.CENTER,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export enum Matpel {
|
export enum Matpel {
|
||||||
_1,
|
_1,
|
||||||
|
@ -6,8 +6,11 @@ import { CertificateDto } from "./certificate.dto";
|
|||||||
import { Persona } from "../../model/Persona/persona.entity";
|
import { Persona } from "../../model/Persona/persona.entity";
|
||||||
import { CursoGIE } from "../../model/CursoGIE/cursoGIE.entity";
|
import { CursoGIE } from "../../model/CursoGIE/cursoGIE.entity";
|
||||||
import { GenerateCertificateDto } from "./dto/GenerateCertificate.dto";
|
import { GenerateCertificateDto } from "./dto/GenerateCertificate.dto";
|
||||||
import { CertData, matpelCert } from "src/certs/MATPEL";
|
import { matpelCert } from "src/certs/MATPEL";
|
||||||
import { Matpel, getMatpelFileName } from "src/certs/utils";
|
import { Matpel, getMatpelFileName } from "src/certs/utils";
|
||||||
|
import { CertData } from "src/certs/CertData";
|
||||||
|
import { getMatpel } from "./generator/matpel";
|
||||||
|
import { getMecanicaBasica } from "./generator/mecanicaBasica";
|
||||||
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@ -68,34 +71,55 @@ export class CertificateService {
|
|||||||
const date = register.fecha_inscripcion.toString();
|
const date = register.fecha_inscripcion.toString();
|
||||||
const [, certYear, certMonth, certDay] = /(\d\d\d\d)-(\d\d)-(\d\d)/.exec(date)!;
|
const [, certYear, certMonth, certDay] = /(\d\d\d\d)-(\d\d)-(\d\d)/.exec(date)!;
|
||||||
|
|
||||||
// Matpel 1, 2 & 3
|
// IDs from db: educa7ls_plataforma > cursosGIE
|
||||||
// TODO: Get actual ids from db instead of hard coding them?
|
switch (register.curso) {
|
||||||
if (register.curso !== 10 && register.curso !== 11 && register.curso !== 12) {
|
// MECANICA BASICA
|
||||||
throw new BadRequestException("Curso no soportado");
|
case 2: {
|
||||||
|
return await getMecanicaBasica(
|
||||||
|
person,
|
||||||
|
register,
|
||||||
|
certYear,
|
||||||
|
certMonth,
|
||||||
|
certDay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// MATPEL 1
|
||||||
|
case 10: {
|
||||||
|
return await getMatpel(
|
||||||
|
Matpel._1,
|
||||||
|
person,
|
||||||
|
register,
|
||||||
|
certYear,
|
||||||
|
certMonth,
|
||||||
|
certDay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// MATPEL 2
|
||||||
|
case 11: {
|
||||||
|
return await getMatpel(
|
||||||
|
Matpel._2,
|
||||||
|
person,
|
||||||
|
register,
|
||||||
|
certYear,
|
||||||
|
certMonth,
|
||||||
|
certDay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// MATPEL 3
|
||||||
|
case 12: {
|
||||||
|
return await getMatpel(
|
||||||
|
Matpel._3,
|
||||||
|
person,
|
||||||
|
register,
|
||||||
|
certYear,
|
||||||
|
certMonth,
|
||||||
|
certDay,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new BadRequestException("Curso no soportado");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let matpel = Matpel._1;
|
|
||||||
if (register.curso === 10) {
|
|
||||||
matpel = Matpel._1;
|
|
||||||
} else if (register.curso === 11) {
|
|
||||||
matpel = Matpel._2;
|
|
||||||
} else if (register.curso === 12) {
|
|
||||||
matpel = Matpel._3;
|
|
||||||
}
|
|
||||||
|
|
||||||
const personFullName = `${person.nombres} ${person.apellidoPaterno} ${person.apellidoMaterno}`;
|
|
||||||
|
|
||||||
const data: CertData = {
|
|
||||||
matpel,
|
|
||||||
personFullName: personFullName,
|
|
||||||
personDni: register.dni,
|
|
||||||
certCode: register.codigo.toString().padStart(4, "0"),
|
|
||||||
certYear,
|
|
||||||
certMonth,
|
|
||||||
certDay,
|
|
||||||
};
|
|
||||||
|
|
||||||
return [await matpelCert(data), `${getMatpelFileName(matpel)} - ${personFullName.toUpperCase()}.docx`];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data: CertificateDto) {
|
async create(data: CertificateDto) {
|
||||||
|
28
src/controller/certificate/generator/matpel.ts
Normal file
28
src/controller/certificate/generator/matpel.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { CertData } from "src/certs/CertData";
|
||||||
|
import { matpelCert } from "src/certs/MATPEL";
|
||||||
|
import { Matpel, getMatpelFileName } from "src/certs/utils";
|
||||||
|
import { Persona } from "src/model/Persona/persona.entity";
|
||||||
|
import { RegistroGIE } from "src/model/RegistroGIE/registroGIE.entity";
|
||||||
|
|
||||||
|
export async function getMatpel(
|
||||||
|
matpel: Matpel,
|
||||||
|
person: Persona,
|
||||||
|
register: RegistroGIE,
|
||||||
|
certYear: string,
|
||||||
|
certMonth: string,
|
||||||
|
certDay: string
|
||||||
|
): Promise<[Buffer, string]> {
|
||||||
|
const personFullName = `${person.nombres} ${person.apellidoPaterno} ${person.apellidoMaterno}`;
|
||||||
|
|
||||||
|
const data: CertData<Matpel> = {
|
||||||
|
matpel,
|
||||||
|
personFullName: personFullName,
|
||||||
|
personDni: register.dni,
|
||||||
|
certCode: register.codigo.toString().padStart(4, "0"),
|
||||||
|
certYear,
|
||||||
|
certMonth,
|
||||||
|
certDay,
|
||||||
|
};
|
||||||
|
|
||||||
|
return [await matpelCert(data), `${getMatpelFileName(matpel)} - ${personFullName.toUpperCase()}.docx`];
|
||||||
|
}
|
26
src/controller/certificate/generator/mecanicaBasica.ts
Normal file
26
src/controller/certificate/generator/mecanicaBasica.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { CertData } from "src/certs/CertData";
|
||||||
|
import { mecanicaBasicaCert } from "src/certs/MECANICA_BASICA";
|
||||||
|
import { Persona } from "src/model/Persona/persona.entity";
|
||||||
|
import { RegistroGIE } from "src/model/RegistroGIE/registroGIE.entity";
|
||||||
|
|
||||||
|
export async function getMecanicaBasica(
|
||||||
|
person: Persona,
|
||||||
|
register: RegistroGIE,
|
||||||
|
certYear: string,
|
||||||
|
certMonth: string,
|
||||||
|
certDay: string
|
||||||
|
): Promise<[Buffer, string]> {
|
||||||
|
const personFullName = `${person.nombres} ${person.apellidoPaterno} ${person.apellidoMaterno}`;
|
||||||
|
|
||||||
|
const data: CertData<null> = {
|
||||||
|
matpel: null,
|
||||||
|
personFullName: personFullName,
|
||||||
|
personDni: register.dni,
|
||||||
|
certCode: register.codigo.toString().padStart(4, "0"),
|
||||||
|
certYear,
|
||||||
|
certMonth,
|
||||||
|
certDay,
|
||||||
|
};
|
||||||
|
|
||||||
|
return [await mecanicaBasicaCert(data), `MECANICA BASICA - ${personFullName.toUpperCase()}.docx`];
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user