[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,
|
||||
HorizontalPositionRelativeFrom, VerticalPositionRelativeFrom,
|
||||
FrameAnchorType,
|
||||
HorizontalPositionAlign,
|
||||
VerticalPositionAlign,
|
||||
TextRun,
|
||||
AlignmentType,
|
||||
} from "docx";
|
||||
import * as QR from "qrcode";
|
||||
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.
|
||||
*
|
||||
* @param props Data to use in the certificate
|
||||
* @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({
|
||||
frame: {
|
||||
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 { join } from "path";
|
||||
import * as QR from "qrcode";
|
||||
|
||||
|
||||
// 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 {
|
||||
_1,
|
||||
|
@ -6,8 +6,11 @@ import { CertificateDto } from "./certificate.dto";
|
||||
import { Persona } from "../../model/Persona/persona.entity";
|
||||
import { CursoGIE } from "../../model/CursoGIE/cursoGIE.entity";
|
||||
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 { CertData } from "src/certs/CertData";
|
||||
import { getMatpel } from "./generator/matpel";
|
||||
import { getMecanicaBasica } from "./generator/mecanicaBasica";
|
||||
|
||||
|
||||
@Injectable()
|
||||
@ -68,34 +71,55 @@ export class CertificateService {
|
||||
const date = register.fecha_inscripcion.toString();
|
||||
const [, certYear, certMonth, certDay] = /(\d\d\d\d)-(\d\d)-(\d\d)/.exec(date)!;
|
||||
|
||||
// Matpel 1, 2 & 3
|
||||
// TODO: Get actual ids from db instead of hard coding them?
|
||||
if (register.curso !== 10 && register.curso !== 11 && register.curso !== 12) {
|
||||
throw new BadRequestException("Curso no soportado");
|
||||
// IDs from db: educa7ls_plataforma > cursosGIE
|
||||
switch (register.curso) {
|
||||
// MECANICA BASICA
|
||||
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) {
|
||||
|
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