@universis/candidates
Version:
Universis api server plugin for study program candidates, internship selection etc
213 lines (209 loc) • 11.9 kB
JavaScript
"use strict";var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
var _stream = require("stream");
var _exceljs = require("exceljs");function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
const XlsxContentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
/**
*
* @param {PostXlsWithConfigOptions} options
* @returns {function(*, *, *): *}
*/
function xlsPostParserWithConfig(options) {
return function (req, res, next) {var _options$name, _options$schema;
let opts = Object.assign({
name: 'file',
schema: 'schema' },
options);
// get file key name
let name = (_options$name = options === null || options === void 0 ? void 0 : options.name) !== null && _options$name !== void 0 ? _options$name : opts.name;
let schema = (_options$schema = options === null || options === void 0 ? void 0 : options.schema) !== null && _options$schema !== void 0 ? _options$schema : opts.schema;
if (req !== null && req !== void 0 && req.files && req !== null && req !== void 0 && req.files[name] && Array.isArray(req === null || req === void 0 ? void 0 : req.files[name]) && req !== null && req !== void 0 && req.files[schema] && Array.isArray(req === null || req === void 0 ? void 0 : req.files[schema])) {
const files = req === null || req === void 0 ? void 0 : req.files[name][0];
const config = req === null || req === void 0 ? void 0 : req.files[schema][0];
let configStream;
try {
configStream = JSON.parse(_fs.default.readFileSync(_path.default.resolve(config.destination, config.filename)));
} catch (err) {
// throw error for invalid json file
return next(err);
}
// get header row, use fixed value if not defined in config
const headerRow = configStream.headerRow || 3;
// get columns
const columns = configStream.columns;
// find max merged column depth, use fixed value if not defined in config
let maxLevel = 0;
columns.forEach(column => {
if (column !== null && column !== void 0 && column.isMerged) {var _column$isMerged;
if (column !== null && column !== void 0 && (_column$isMerged = column.isMerged) !== null && _column$isMerged !== void 0 && _column$isMerged.value) {
const level = column.isMerged.level;
if (level > maxLevel) {
maxLevel = level;
}
}
}
});
if (files.mimetype === XlsxContentType || /\.(xlsx)$/i.test(files.originalname) && files.mimetype === 'application/octet-stream') {
let stream;
if (files.buffer instanceof Buffer) {
stream = new _stream.Readable();
stream.push(files.buffer);
stream.push(null);
} else {
try {
stream = _fs.default.createReadStream(_path.default.resolve(files.destination, files.filename));
} catch (err) {
return next(err);
}
}
let body = [],headers = [];
// read from a file
let workbook = new _exceljs.Workbook();
return workbook.xlsx.read(stream).
then(() => {
// read workbook
let sheet = workbook.getWorksheet(1);
sheet.eachRow((row, rowNumber) => {
if (rowNumber >= headerRow) {
if (rowNumber === headerRow + maxLevel) {
headers = row.values;
// replace whitespace in headers with a single space
headers = headers.map(x => x.replace(/\s/g, " ").trim());
} else {
let res = {};
row.values.forEach((v, k) => {
if (k > 0) {var _columnInConfig$prope;
const columnInConfig = columns.find(column => {var _column$property;return (column === null || column === void 0 ? void 0 : (_column$property = column.property) === null || _column$property === void 0 ? void 0 : _column$property.title.trim()) === headers[k];});
// if property is virtual (not used by model), return
if (columnInConfig !== null && columnInConfig !== void 0 && columnInConfig.virtual) {
return;
}
// if it is needed but is not mapped to a model attribute, throw error
if (!(columnInConfig !== null && columnInConfig !== void 0 && (_columnInConfig$prope = columnInConfig.property) !== null && _columnInConfig$prope !== void 0 && _columnInConfig$prope.modelAttribute)) {
return next(new Error(`Invalid configuration. Model attribute is not defined for column ${headers[k]}`));
}
if (!(columnInConfig !== null && columnInConfig !== void 0 && columnInConfig.dataMappings)) {var _v, _v2, _columnInConfig$prope2;
if (typeof v === 'object' && (_v = v) !== null && _v !== void 0 && _v.text && (_v2 = v) !== null && _v2 !== void 0 && _v2.hyperlink) {
v = v.text;
}
// attributes may be like "property/childProperty for nested objects.
// this makes sure that the correct object is created by splitting the
// model attribute to property and childProperty and then appends the
// the childProperties to the parent object.
if (columnInConfig !== null && columnInConfig !== void 0 && (_columnInConfig$prope2 = columnInConfig.property) !== null && _columnInConfig$prope2 !== void 0 && _columnInConfig$prope2.modelAttribute.includes('/')) {var _columnInConfig$prope3, _columnInConfig$prope4, _columnInConfig$prope5, _columnInConfig$prope6;
if (!res[columnInConfig === null || columnInConfig === void 0 ? void 0 : (_columnInConfig$prope3 = columnInConfig.property) === null || _columnInConfig$prope3 === void 0 ? void 0 : _columnInConfig$prope3.modelAttribute.split('/')[0]])
res[columnInConfig === null || columnInConfig === void 0 ? void 0 : (_columnInConfig$prope4 = columnInConfig.property) === null || _columnInConfig$prope4 === void 0 ? void 0 : _columnInConfig$prope4.modelAttribute.split('/')[0]] = {};
if (typeof v === 'string') {
v = v.trim();
if (v.length === 0) {
// convert empty string to null
v = null;
}
}
res[columnInConfig === null || columnInConfig === void 0 ? void 0 : (_columnInConfig$prope5 = columnInConfig.property) === null || _columnInConfig$prope5 === void 0 ? void 0 : _columnInConfig$prope5.modelAttribute.split('/')[0]][columnInConfig === null || columnInConfig === void 0 ? void 0 : (_columnInConfig$prope6 = columnInConfig.property) === null || _columnInConfig$prope6 === void 0 ? void 0 : _columnInConfig$prope6.modelAttribute.split('/')[1]] = v;
} else {var _columnInConfig$prope7;
if (typeof v === 'string') {
v = v.trim();
if (v.length === 0) {
// convert empty string to null
v = null;
}
}
res[columnInConfig === null || columnInConfig === void 0 ? void 0 : (_columnInConfig$prope7 = columnInConfig.property) === null || _columnInConfig$prope7 === void 0 ? void 0 : _columnInConfig$prope7.modelAttribute] = v;
}
} else {var _columnInConfig$prope8, _configStream;
// get data mappings
let mappings = columnInConfig.dataMappings;
// find from value
const value = mappings.find(x => x.from == v);
// if it is not defined, throw error
if (!value) {
return next(new Error(`Invalid configuration. Incorrect or missing data mappings for column ${headers[k]}`));
}
if (res[columnInConfig === null || columnInConfig === void 0 ? void 0 : (_columnInConfig$prope8 = columnInConfig.property) === null || _columnInConfig$prope8 === void 0 ? void 0 : _columnInConfig$prope8.modelAttribute]) {var _columnInConfig$prope9, _columnInConfig$prope10;
// append to object if it already exists
res[columnInConfig === null || columnInConfig === void 0 ? void 0 : (_columnInConfig$prope9 = columnInConfig.property) === null || _columnInConfig$prope9 === void 0 ? void 0 : _columnInConfig$prope9.modelAttribute] = { ...value.to, ...res[columnInConfig === null || columnInConfig === void 0 ? void 0 : (_columnInConfig$prope10 = columnInConfig.property) === null || _columnInConfig$prope10 === void 0 ? void 0 : _columnInConfig$prope10.modelAttribute] };
} else {var _columnInConfig$prope11;
res[columnInConfig === null || columnInConfig === void 0 ? void 0 : (_columnInConfig$prope11 = columnInConfig.property) === null || _columnInConfig$prope11 === void 0 ? void 0 : _columnInConfig$prope11.modelAttribute] = value.to;
}
// append extra attributes
if ((_configStream = configStream) !== null && _configStream !== void 0 && _configStream.extraAttributes) {
configStream.extraAttributes.forEach(x => {
res[x.modelAttribute] = x.defaultValue;
});
}
}
}
});
body.push(res);
}
}
});
req.body = body;
return next();
}).catch(err => {
return next(err);
});
}
return next();
} else {
return next(new Error("Missing candidate students xlsx file or its json schema."));
}
};
}
/**
* Allows data conversion to xls, independent from res. Similar to xlsParser function
* @param {*} data The data to be converted
* @returns Xlsx buffer
*/
function toXlsx(data) {
return new Promise((resolve, reject) => {
if (Array.isArray(data)) {
const workbook = new _exceljs.Workbook();
const sheet = workbook.addWorksheet('Sheet1');
let rows = data.map(row => {
let res = {};
Object.keys(row).forEach(key => {
// if attribute is an object
if (typeof row[key] === 'object' && row[key] !== null) {
// if attribute has property name
if (row[key].hasOwnProperty('name')) {
// return this name
res[key] = row[key]['name'];
} else
{
// otherwise return object
res[key] = row[key];
}
} else
{
// set property value
res[key] = row[key];
}
});
return res;
});
// add columns
if (rows.length > 0) {
sheet.columns = Object.keys(rows[0]).map(key => {
return { header: key, key: key };
});
}
rows.forEach(row => {
sheet.addRow(row);
});
// write to a new buffer
return workbook.xlsx.writeBuffer().then(buffer => {
return resolve(buffer);
}).catch(err => {
return reject(err);
});
} else {
return reject('Unprocessable entity');
}
});
}
module.exports.xlsPostParserWithConfig = xlsPostParserWithConfig;
module.exports.XlsxContentType = XlsxContentType;
module.exports.toXlsx = toXlsx;
//# sourceMappingURL=xls.js.map