Skip to content

Commit

Permalink
feat: ✨ Adding template export in xlsx format feature
Browse files Browse the repository at this point in the history
  • Loading branch information
susanakasato authored and LucasTonetto committed Jun 25, 2021
1 parent 44ad6a4 commit 881160f
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 65 deletions.
72 changes: 72 additions & 0 deletions dist/models/TemplateExcel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TemplateExcel = void 0;
const exceljs_1 = require("exceljs");
const StringUtils_1 = require("../utils/StringUtils");
class TemplateExcel {
constructor(config) {
this._config = config;
this._validationRules = config.validationRules;
this._columns = Object.keys(this._validationRules);
this._workbook = this.createTemplateExcel();
}
createTemplateExcel() {
const workbook = new exceljs_1.Workbook();
this.addExcelCoverWorksheet(workbook);
this.addExcelValidationWorksheet(workbook);
this.addExcelTemplateWorksheet(workbook);
return workbook;
}
getExcelBuffer() {
return this._workbook.xlsx.writeBuffer({ filename: 'template.xlsx' });
}
addExcelCoverWorksheet(workbook) {
const coverSheet = workbook.addWorksheet('cover');
coverSheet.getCell('A1').value = `Template downloaded on: ${new Date()}`;
coverSheet.getCell('A2').value = `Configuration version: ${this._config.version}`;
}
addExcelValidationWorksheet(workbook) {
const validationTemplate = workbook.addWorksheet('validation');
this.setExcelWorksheetHeader(validationTemplate);
this.setValidationExcelWorksheetValues(validationTemplate);
}
addExcelTemplateWorksheet(workbook) {
const templateSheet = workbook.addWorksheet('template');
this.setExcelWorksheetHeader(templateSheet);
this.setTemplateExcelWorksheetDataValidation(templateSheet);
}
setExcelWorksheetHeader(worksheet) {
const columns = ["url", ...this._columns];
worksheet.columns = columns.map(column => {
return {
header: column,
key: column,
width: 20
};
});
}
setValidationExcelWorksheetValues(validationWorksheet) {
this._columns.forEach(column => {
validationWorksheet.getColumn(column).values = [column, ...this._validationRules[column]];
});
}
setTemplateExcelWorksheetDataValidation(templateWorksheet) {
this._columns.forEach(column => {
if (!StringUtils_1.StringUtils.isStringForRegex(this._validationRules[column][0])) {
const validationCount = this._validationRules[column].length;
let columnLetter = templateWorksheet.getColumn(column).letter;
for (let i = 2; i < 1003; i++) {
templateWorksheet.getCell(columnLetter + i).dataValidation = {
type: 'list',
allowBlank: false,
formulae: [`=validation!$${columnLetter}$2:$${columnLetter}$${validationCount + 1}`]
};
}
}
});
}
checkRegex(string) {
return string.match(/^\/.*\/$/) !== null;
}
}
exports.TemplateExcel = TemplateExcel;
86 changes: 58 additions & 28 deletions dist/routes/template.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,61 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const ConfigDAO_1 = require('../models/DAO/ConfigDAO');
const ApiResponse_1 = require('../models/ApiResponse');
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const ConfigDAO_1 = require("../models/DAO/ConfigDAO");
const ApiResponse_1 = require("../models/ApiResponse");
const TemplateExcel_1 = require("../models/TemplateExcel");
const template = (app) => {
app.get('/template', (req, res) => {
const configDAO = new ConfigDAO_1.ConfigDAO('company');
const apiResponse = new ApiResponse_1.ApiResponse();
configDAO
.getLastConfig()
.then((config) => {
res.setHeader('Content-disposition', 'attachment; filename=template.csv');
res.set('Content-Type', 'text/csv; charset=utf-8');
apiResponse.statusCode = 200;
apiResponse.responseText = config.toCsvTemplate();
})
.catch((err) => {
apiResponse.responseText = 'Erro ao recuperar a configuração!';
apiResponse.statusCode = 500;
apiResponse.errorMessage = err.message;
})
.finally(() => {
if (apiResponse.statusCode === 200) {
res.status(apiResponse.statusCode).send(apiResponse.responseText);
} else {
res.status(apiResponse.statusCode).send(apiResponse.jsonResponse);
}
});
});
app.get('/template', (req, res) => {
const company = req.company;
const configDAO = new ConfigDAO_1.ConfigDAO('company');
const apiResponse = new ApiResponse_1.ApiResponse();
configDAO
.getLastConfig()
.then((config) => {
res.setHeader('Content-disposition', 'attachment; filename=template.csv');
res.set('Content-Type', 'text/csv; charset=utf-8');
apiResponse.statusCode = 200;
apiResponse.responseText = config.toCsvTemplate();
})
.catch((err) => {
apiResponse.responseText = 'Erro ao recuperar a configuração!';
apiResponse.statusCode = 500;
apiResponse.errorMessage = err.message;
})
.finally(() => {
if (apiResponse.statusCode === 200) {
res.status(apiResponse.statusCode).send(apiResponse.responseText);
}
else {
res.status(apiResponse.statusCode).send(apiResponse.jsonResponse);
}
});
});
app.get('/template/excel', (req, res) => {
const company = req.company;
const configDAO = new ConfigDAO_1.ConfigDAO(company);
const apiResponse = new ApiResponse_1.ApiResponse();
configDAO
.getLastConfig()
.then((config) => {
const templateExcel = new TemplateExcel_1.TemplateExcel(config);
templateExcel.getExcelBuffer()
.then((buffer) => {
apiResponse.statusCode = 200;
res.contentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.attachment('template.xlsx');
res.send(buffer);
}).catch((err) => {
apiResponse.responseText = 'Erro ao baixar o template!';
apiResponse.statusCode = 500;
apiResponse.errorMessage = err.message;
res.status(apiResponse.statusCode).send(apiResponse.jsonResponse);
});
}).catch((err) => {
apiResponse.responseText = 'Erro ao recuperar a configuração!';
apiResponse.statusCode = 500;
apiResponse.errorMessage = err.message;
res.status(apiResponse.statusCode).send(apiResponse.jsonResponse);
});
});
};
exports.default = template;
69 changes: 34 additions & 35 deletions dist/utils/StringUtils.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,38 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.StringUtils = void 0;
class StringUtils {
static normalize(string) {
return string
.toLowerCase()
.replace(/[áàãâä]/gi, 'a')
.replace(/[éèêë]/gi, 'e')
.replace(/[íìîï]/gi, 'i')
.replace(/[óòõôö]/gi, 'o')
.replace(/[úùûü]/gi, 'u')
.replace(/[ç]/gi, 'c');
}
static replaceWhiteSpace(string, replacer) {
return string.replace(/ /g, replacer);
}
static _isStringForRegex(string) {
return string[0] === '/' && string[string.length - 1] === '/';
}
static validateString(stringToValidate, rules, separator = ' ') {
const validate = [false];
rules.forEach((rule) => {
if (this._isStringForRegex(rule)) {
const regexRule = new RegExp(rule.slice(1, rule.length - 1));
validate.push(!!stringToValidate.match(regexRule));
} else {
validate.push(
stringToValidate.toLocaleLowerCase() === this.replaceWhiteSpace(rule.toLocaleLowerCase(), separator)
);
}
});
return validate.indexOf(true) !== -1;
}
static isEmpty(string) {
return !string;
}
static normalize(string) {
return string
.toLowerCase()
.replace(/[áàãâä]/gi, 'a')
.replace(/[éèêë]/gi, 'e')
.replace(/[íìîï]/gi, 'i')
.replace(/[óòõôö]/gi, 'o')
.replace(/[úùûü]/gi, 'u')
.replace(/[ç]/gi, 'c');
}
static replaceWhiteSpace(string, replacer) {
return string.replace(/ /g, replacer);
}
static isStringForRegex(string) {
return string[0] === '/' && string[string.length - 1] === '/';
}
static validateString(stringToValidate, rules, separator = ' ') {
const validate = [false];
rules.forEach((rule) => {
if (this.isStringForRegex(rule)) {
const regexRule = new RegExp(rule.slice(1, rule.length - 1));
validate.push(!!stringToValidate.match(regexRule));
}
else {
validate.push(stringToValidate.toLocaleLowerCase() === this.replaceWhiteSpace(rule.toLocaleLowerCase(), separator));
}
});
return validate.indexOf(true) !== -1;
}
static isEmpty(string) {
return !string;
}
}
exports.StringUtils = StringUtils;
112 changes: 112 additions & 0 deletions src/ts/models/TemplateExcel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Buffer, Workbook, Worksheet } from 'exceljs';
import { StringUtils } from '../utils/StringUtils';
import { Config } from './Config';

export class TemplateExcel {
private _config: Config;
private _validationRules: { [key:string]: string[] };
private _columns: string[];
private _workbook: Workbook;

constructor(config: Config) {
this._config = config;
this._validationRules = config.validationRules;
this._columns = Object.keys(this._validationRules);
this._workbook = this.createTemplateExcel();
}

/**
* Criar o template em formato Excel .xlsx.
* @returns Workbook Excel com abas 'Template' e 'Validation' populados de acordo com a configuração de validação do AdInfo.
*/
private createTemplateExcel(): Workbook {
const workbook = new Workbook();
this.addExcelCoverWorksheet(workbook);
this.addExcelValidationWorksheet(workbook);
this.addExcelTemplateWorksheet(workbook);
return workbook;
}

/**
* Criar um buffer a partir do Excel gerado na construção da classe.
* @returns Uma Promise que retorna um Buffer a partir do Excel gerado na construção da classe.
*/
public getExcelBuffer(): Promise<Buffer> {
return this._workbook.xlsx.writeBuffer({filename: 'template.xlsx'});
}

/**
* Gerar a aba 'cover' no Excel Template, contendo informações do template, como data de download e versão da configuração.
* @param workbook Objeto Workbook no qual a aba será gerada.
*/
private addExcelCoverWorksheet(workbook: Workbook): void {
const coverSheet = workbook.addWorksheet('cover');
coverSheet.getCell('A1').value = `Template downloaded on: ${new Date()}`;
coverSheet.getCell('A2').value = `Configuration version: ${this._config.version}`
}

/**
* Gerar a aba de 'Validation' no Excel Template, com os valores populados para validação.
* @param workbook Objeto Workbook no qual a aba será gerada.
*/
private addExcelValidationWorksheet(workbook: Workbook): void {
const validationTemplate = workbook.addWorksheet('validation');
this.setExcelWorksheetHeader(validationTemplate);
this.setValidationExcelWorksheetValues(validationTemplate);
}

/**
* Gerar a aba de 'Template' no Excel Template, com os campos e validações populados para uso.
* @param workbook Objeto Workbook no qual a aba será gerada.
*/
private addExcelTemplateWorksheet(workbook: Workbook): void {
const templateSheet = workbook.addWorksheet('template');
this.setExcelWorksheetHeader(templateSheet);
this.setTemplateExcelWorksheetDataValidation(templateSheet);
}

/**
* Nomear as colunas e inserir os valores dos campos na aba do Excel, com os valores de acordo com a configuração de validação do AdInfo.
* @param worksheet Objeto Worksheet (aba do Excel) no qual terá os campos inseridos e as colunas nomeadas.
*/
private setExcelWorksheetHeader(worksheet: Worksheet): void {
const columns = ["url", ...this._columns];
worksheet.columns = columns.map(column => {
return {
header: column,
key: column,
width: 20
}
})
}

/**
* Inserir o nome do campo e os valores de cada campo que serão usados para validação de dados, de acordo com a configuração de validação do AdInfo.
* @param validationWorksheet Objeto Worksheet (aba do Excel) referente à aba de validação.
*/
private setValidationExcelWorksheetValues(validationWorksheet: Worksheet): void {
this._columns.forEach(column => {
validationWorksheet.getColumn(column).values = [column, ...this._validationRules[column]];
})
}

/**
* Aplicar a validação de dados nos campos do template, de acordo com a configuração de validação do AdInfo, para as primeiras 1000 linhas.
* @param templateWorksheet Objecto worksheet (aba do Excel) referente à aba de template.
*/
private setTemplateExcelWorksheetDataValidation(templateWorksheet: Worksheet): void {
this._columns.forEach(column => {
if (!StringUtils.isStringForRegex(this._validationRules[column][0])) {
const validationCount = this._validationRules[column].length;
let columnLetter = templateWorksheet.getColumn(column).letter;
for(let i = 2; i < 1003; i++) {
templateWorksheet.getCell(columnLetter + i).dataValidation = {
type: 'list',
allowBlank: false,
formulae: [`=validation!$${columnLetter}$2:$${columnLetter}$${validationCount + 1}`]
};
}
}
})
}
}
29 changes: 29 additions & 0 deletions src/ts/routes/template.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ConfigDAO } from '../models/DAO/ConfigDAO';
import { ApiResponse } from '../models/ApiResponse';
import { Config } from '../models/Config';
import { TemplateExcel } from '../models/TemplateExcel';

const template = (app: { [key: string]: any }): void => {
app.get('/template', (req: { [key: string]: any }, res: { [key: string]: any }) => {
Expand Down Expand Up @@ -29,6 +30,34 @@ const template = (app: { [key: string]: any }): void => {
}
});
});

app.get('/template/excel', (req: { [key: string]: any }, res: { [key: string]: any }) => {
const company = req.company;
const configDAO = new ConfigDAO(company);
const apiResponse = new ApiResponse();
configDAO
.getLastConfig()
.then((config: Config) => {
const templateExcel = new TemplateExcel(config);
templateExcel.getExcelBuffer()
.then((buffer: Buffer) => {
apiResponse.statusCode = 200;
res.contentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.attachment('template.xlsx');
res.send(buffer);
}).catch((err) => {
apiResponse.responseText = 'Erro ao baixar o template!';
apiResponse.statusCode = 500;
apiResponse.errorMessage = err.message;
res.status(apiResponse.statusCode).send(apiResponse.jsonResponse);
})
}).catch((err) => {
apiResponse.responseText = 'Erro ao recuperar a configuração!';
apiResponse.statusCode = 500;
apiResponse.errorMessage = err.message;
res.status(apiResponse.statusCode).send(apiResponse.jsonResponse);
})
});
};

export default template;
Loading

0 comments on commit 881160f

Please sign in to comment.