[docx] Start MECANICA BASICA generation

master
Fernando 2023-05-18 10:04:38 -05:00
parent 74f8009083
commit c46fbd3c26
8 changed files with 386 additions and 69 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 709 KiB

38
src/certs/CertData.ts Normal file
View 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,
}

View File

@ -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: {

View 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);
}

View File

@ -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,

View File

@ -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");
}
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"),
// IDs from db: educa7ls_plataforma > cursosGIE
switch (register.curso) {
// MECANICA BASICA
case 2: {
return await getMecanicaBasica(
person,
register,
certYear,
certMonth,
certDay,
};
return [await matpelCert(data), `${getMatpelFileName(matpel)} - ${personFullName.toUpperCase()}.docx`];
);
}
// 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");
}
}
}
async create(data: CertificateDto) {

View 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`];
}

View 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`];
}