initial commit - code moved to separate repo
This commit is contained in:
822
src/helpers/excel.js
Normal file
822
src/helpers/excel.js
Normal file
@ -0,0 +1,822 @@
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const dotenv = require("dotenv");
|
||||
dotenv.config();
|
||||
// dotenv.config({ path: ".env.local" });
|
||||
|
||||
const { Shift, Publisher, PrismaClient } = require("@prisma/client");
|
||||
const CON = require("./const");
|
||||
const common = require("./common");
|
||||
const data = require("./data");
|
||||
//const { fi } = require("date-fns/locale");
|
||||
|
||||
//Works with nextjs, but fails with nodejs
|
||||
|
||||
// for nodejs
|
||||
//const api = require("./pages/api/index");
|
||||
|
||||
exports.GenerateExcel = async function (req, res) {
|
||||
|
||||
const prisma = common.getPrismaClient();
|
||||
const year = req.params.year;
|
||||
const month = parseInt(req.params.month) - 1;
|
||||
const fromDate = new Date(year, month, 1); // month is 0 based
|
||||
// to last day of the month. special case december
|
||||
const toDate = new Date(year, month + 1, 0); // month is 0 based
|
||||
// toDate.setMonth(fromDate.getMonth() + 1);
|
||||
|
||||
//get all shiifts for the month
|
||||
var shifts = await prisma.shift.findMany({
|
||||
where: {
|
||||
startTime: {
|
||||
gte: fromDate,
|
||||
lt: toDate,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
cartEvent: {
|
||||
include: {
|
||||
location: true,
|
||||
},
|
||||
},
|
||||
publishers: true,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
var filePath = path.join(CON.contentPath, "График КОЛИЧКИ.xlsx");
|
||||
const bеgin = new Date();
|
||||
|
||||
//----------------- exit "График КОЛИЧКИ.xlsx" with exceljs ----------------
|
||||
const ExcelJS = require("exceljs");
|
||||
const xjswb = new ExcelJS.Workbook();
|
||||
if (req.params.process == "1") {
|
||||
try {
|
||||
xjswb.xlsx
|
||||
.readFile(filePath)
|
||||
.then(function () {
|
||||
try {
|
||||
var worksheet = xjswb.getWorksheet(13);
|
||||
|
||||
//get row 2 with all the styles
|
||||
var weekHeader = worksheet.getRow(2);
|
||||
|
||||
var newWorksheet = xjswb.addWorksheet(
|
||||
CON.monthNamesBG[month]
|
||||
);
|
||||
newWorksheet.name = CON.monthNamesBG[month].toUpperCase();
|
||||
//copy each row from the template with all the styles
|
||||
worksheet.eachRow(
|
||||
{ includeEmpty: true },
|
||||
function (row, rowNumber) {
|
||||
var newRow = newWorksheet.getRow(rowNumber);
|
||||
newRow.height = row.height;
|
||||
row.eachCell(
|
||||
{ includeEmpty: true },
|
||||
function (cell, colNumber) {
|
||||
var newCell = newRow.getCell(colNumber);
|
||||
newCell.value = cell.value;
|
||||
newCell.font = cell.font;
|
||||
newCell.alignment = cell.alignment;
|
||||
newCell.border = cell.border;
|
||||
newCell.fill = cell.fill;
|
||||
newCell.numberFormat = cell.numberFormat;
|
||||
newCell.protection = cell.protection;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
// worksheet.eachRow({ includeEmpty: true }, function (row, rowNumber) {
|
||||
// row.eachCell({ includeEmpty: true }, function (cell, colNumber) {
|
||||
// newWorksheet.getCell(rowNumber, colNumber).value = cell.value;
|
||||
// });
|
||||
// });
|
||||
for (let i = start.row; i <= end.row; i++) {
|
||||
const leftBorderCell = worksheet.getCell(i, start.col);
|
||||
|
||||
//hide original sheet
|
||||
worksheet.state = "hidden";
|
||||
//save file
|
||||
xjswb.xlsx.writeFile(
|
||||
path.join(
|
||||
contentPath,
|
||||
`График КОЛИЧКИ ${year}-${month + 1}.xlsx`
|
||||
)
|
||||
);
|
||||
//send the file to the client
|
||||
res.setHeader(
|
||||
"Content-Type",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
);
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
"attachment; filename=" +
|
||||
encodeURI(`График КОЛИЧКИ ${year}-${month + 1}.xlsx`)
|
||||
);
|
||||
xjswb.xlsx.write(res);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
res.end(
|
||||
"[" +
|
||||
new Date().toLocaleString() +
|
||||
"] (" +
|
||||
(new Date() - bеgin) +
|
||||
"ms) " +
|
||||
err
|
||||
);
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
console.log("done");
|
||||
//show cyrillic text in response
|
||||
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||
res.end(
|
||||
"Генериране на График КОЛИЧКИ${year}-${month}.xlsx завършено успешно за " +
|
||||
(new Date() - bеgin) +
|
||||
"ms"
|
||||
);
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
res.end(err.message);
|
||||
}
|
||||
}
|
||||
//----------------- exit "График КОЛИЧКИ.xlsx" with xlsx-style ----------------
|
||||
if (req.params.process == "2") {
|
||||
const XLSX = require('xlsx');
|
||||
const wb = XLSX.utils.book_new();
|
||||
wb.Props = {
|
||||
Title: "График КОЛИЧКИ",
|
||||
Subject: "График КОЛИЧКИ",
|
||||
};
|
||||
wb.SheetNames.push("График КОЛИЧКИ");
|
||||
const ws_data = [
|
||||
[1, 2, 3],
|
||||
[true, false, null, "sheetjs"],
|
||||
["foo", "bar", new Date("2014-02-19T14:30Z"), "0.3"],
|
||||
["baz", null, "qux"]
|
||||
];
|
||||
const ws = XLSX.utils.aoa_to_sheet(ws_data);
|
||||
wb.Sheets["График КОЛИЧКИ"] = ws;
|
||||
|
||||
const xlsxstyle = require("xlsx-style");
|
||||
try {
|
||||
const workbook = xlsxstyle.readFile(filePath);
|
||||
const sheetNames = workbook.SheetNames;
|
||||
//find the sheet with the name "Зима" in sheetNames
|
||||
const sheetName = sheetNames.find((name) => name === "Зима");
|
||||
|
||||
// Get the data of "Sheet1"
|
||||
const data = xlsxstyle.utils.sheet_to_json(workbook.Sheets[sheetNames[2]]);
|
||||
|
||||
var worksheet = workbook.Sheets[sheetName];
|
||||
//copy worksheet to new workbook
|
||||
//add new worksheet to new workbook with month name
|
||||
// var newWorksheet = wb.addWorksheet(CON.monthNamesBG[month]);
|
||||
var rows = xlsxstyle.utils.sheet_to_row_object_array(worksheet, {
|
||||
header: 1,
|
||||
});
|
||||
|
||||
XLSX.utils.book_append_sheet(wb, worksheet, "_" + CON.monthNamesBG[month]);
|
||||
|
||||
//save file
|
||||
XLSX.writeFile(wb, path.join(CON.contentPath, `_График КОЛИЧКИ ${year}-${month + 1}.xlsx`));
|
||||
|
||||
|
||||
//save file
|
||||
xlsxstyle.writeFile(
|
||||
newWorkbook,
|
||||
path.join(
|
||||
contentPath,
|
||||
`График КОЛИЧКИ ${year}-${month + 1}.xlsx`
|
||||
)
|
||||
);
|
||||
//send the file to the client
|
||||
res.setHeader(
|
||||
"Content-Type",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
);
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
"attachment; filename=" +
|
||||
encodeURI(`График КОЛИЧКИ ${year}-${month + 1}.xlsx`)
|
||||
);
|
||||
xlsxstyle.writeFile(newWorkbook, res);
|
||||
|
||||
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
||||
res.end(
|
||||
"Генериране на График КОЛИЧКИ${year}-${month}.xlsx завършено успешно за " +
|
||||
(new Date() - bеgin) +
|
||||
" ms!"
|
||||
);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
res.end("[" + new Date().toLocaleString() + "] " + err);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------- exit "График КОЛИЧКИ.xlsx" with node-excel-export ----------------
|
||||
if (req.params.process == "3") {
|
||||
// // uses xlsx-style in the background
|
||||
// // https://github.com/protobi/js-xlsx#cell-styles
|
||||
// // https://www.npmjs.com/package/node-excel-export
|
||||
// var excel = require('node-excel-export');
|
||||
// var rows = [[
|
||||
// { value: "EXTREMELY LONG TITLE 1", bold: 1, autoWidth: true },
|
||||
// { value: "TITLE2" },
|
||||
// { value: "TITLE3" }
|
||||
// ]];
|
||||
// var styles = {
|
||||
// headerHilight: {
|
||||
// fill: {
|
||||
// fgColor: {
|
||||
// rgb: 'FFE36600'
|
||||
// }
|
||||
// },
|
||||
// font: {
|
||||
// color: {
|
||||
// rgb: 'FFFFFFFF'
|
||||
// },
|
||||
// sz: 10,
|
||||
// bold: true,
|
||||
// // underline: true
|
||||
// }
|
||||
// },
|
||||
// cellOdd: {
|
||||
// fill: {
|
||||
// fgColor: {
|
||||
// rgb: 'FFF8F8F7'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
// const heading = [
|
||||
// [{value: 'b1', style: styles.headerHilight},
|
||||
// {value: 'd1', style: styles.headerHilight},
|
||||
// {value: 'e1', style: styles.headerHilight}],
|
||||
// ['b2', 'd2', 'e2'] // <-- It can be only values
|
||||
// ];
|
||||
// var specification = {
|
||||
// "shiftTime": {
|
||||
// displayName: 'Смяна',
|
||||
// headerStyle: styles.headerHilight,
|
||||
// cellStyle: styles.cellOdd,
|
||||
// width: 60
|
||||
// },
|
||||
// "publisherName": {
|
||||
// "displayName": 'ПЛИСКА ПОНЕДЕЛНИК',
|
||||
// "headerStyle": styles.headerHilight,
|
||||
// "width": 250
|
||||
// },
|
||||
// "Col2": {
|
||||
// "displayName": 'СТАДИОН СРЯДА',
|
||||
// "headerStyle": styles.headerHilight,
|
||||
// "width": 215
|
||||
// },
|
||||
// "Col3": {
|
||||
// displayName: 'УНИВЕРСИТЕТ ЧЕТВЪРТЪК',
|
||||
// headerStyle: styles.headerHilight,
|
||||
// width: 150
|
||||
// }
|
||||
// }
|
||||
// var report = excel.buildExport(
|
||||
// [{
|
||||
// name: `График КОЛИЧКИ ${year}-${month + 1}.xlsx`,
|
||||
// specification: specification,
|
||||
// heading: heading, // <- Raw heading array (optional)
|
||||
// data: rows
|
||||
// }]);
|
||||
// //save file to disk
|
||||
// fs.writeFile(path.join(contentPath, `График КОЛИЧКИ ${year}-${month + 1}.xlsx`), report, 'binary', function (err) { });
|
||||
// res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
// res.end("Генериране на График КОЛИЧКИ${year}-${month}.xlsx завършено успешно за " + (new Date() - bеgin) + " ms!");
|
||||
// //send the file to the client
|
||||
// console.log("excel genarated in " + (new Date() - bеgin) + "ms");
|
||||
// // res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
||||
// // res.setHeader("Content-Disposition", "attachment; filename=" + encodeURI(`График КОЛИЧКИ ${year}-${month + 1}.xlsx`));
|
||||
// // res.end(report);
|
||||
}
|
||||
}
|
||||
|
||||
exports.ImportFromExcel = function (req, res) {
|
||||
}
|
||||
|
||||
exports.processEvents = async function (events, year, monthNumber, progressCallback, createAvailabilities) {
|
||||
|
||||
const prisma = common.getPrismaClient();
|
||||
const d = new Date(year, monthNumber - 1);//month is 0 based in js
|
||||
const monthDatesInfo = common.getMonthDatesInfo(d); //CAL.GetMonthDatesInfo(date);
|
||||
try {
|
||||
await prisma.shift.deleteMany({
|
||||
where: {
|
||||
startTime: {
|
||||
gte: monthDatesInfo.firstMonday,
|
||||
lt: monthDatesInfo.lastSunday,
|
||||
},
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
var shifts = await prisma.shift.findMany({
|
||||
where: {
|
||||
isactive: true,
|
||||
startTime: {
|
||||
gte: monthDatesInfo.firstMonday,
|
||||
lt: monthDatesInfo.lastSunday,
|
||||
},
|
||||
}
|
||||
});
|
||||
var locations = await prisma.location.findMany({ where: { isactive: true, } });
|
||||
|
||||
var cartEvents = await prisma.cartEvent.findMany({ where: { isactive: true, } });
|
||||
|
||||
var publishers = await prisma.publisher.findMany({
|
||||
where: { isactive: true, },
|
||||
include: {
|
||||
availabilities: { where: { isactive: true, }, },
|
||||
assignments: { include: { shift: true, }, },
|
||||
},
|
||||
});
|
||||
|
||||
const totalEvents = events.length;
|
||||
|
||||
for (let i = 0; i < totalEvents; i++) {
|
||||
const event = events[i];
|
||||
const progress = (i / totalEvents) * 100;
|
||||
if (progress > 1) {
|
||||
progressCallback(progress);
|
||||
}
|
||||
try {
|
||||
const date = new Date(event.date);
|
||||
let startStr, endStr;
|
||||
if (event.time) {
|
||||
startStr = event.time.split("-")[0].trim();
|
||||
endStr = event.time.split("-")[1].trim();
|
||||
}
|
||||
else {
|
||||
//get the start event time and event.shiftNr and calculate start and end based on that
|
||||
const shift = shifts.find((s) => s.nr === event.shiftNr);
|
||||
if (!shift) {
|
||||
console.warn(`Could not find shift with nr '${event.shiftNr}'`);
|
||||
continue;
|
||||
}
|
||||
startStr = shift.startTime;
|
||||
endStr = shift.endTime;
|
||||
|
||||
}
|
||||
let st = new Date(event.date);
|
||||
st.setHours(startStr.split(":")[0]);
|
||||
st.setMinutes(startStr.split(":")[1]);
|
||||
const start = st;
|
||||
st = new Date(event.date);
|
||||
st.setHours(endStr.split(":")[0]);
|
||||
st.setMinutes(endStr.split(":")[1]);
|
||||
const end = st
|
||||
|
||||
|
||||
var location = locations.find((l) =>
|
||||
l.name.toLowerCase().includes(event.placeOfEvent.toLowerCase())
|
||||
);
|
||||
if (!location) {
|
||||
console.warn(`Could not find location with name '${event.placeOfEvent}'`);
|
||||
//await prisma.location.create({ data: { name: event.placeOfEvent } });
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
var dayofWeek = common.getDayOfWeekNameEnEnum(date);
|
||||
const cartEvent = cartEvents.find(
|
||||
(ce) =>
|
||||
ce.locationId === location.id &&
|
||||
ce.dayofweek === dayofWeek
|
||||
);
|
||||
|
||||
if (!cartEvent) {
|
||||
console.warn(`Could not find cart event for date '${date}' and location '${event.placeOfEvent}'`);
|
||||
continue;
|
||||
}
|
||||
|
||||
let shift = shifts.find((s) =>
|
||||
s.cartEventId === cartEvent.id &&
|
||||
new Date(s.startTime).getTime() === new Date(start).getTime()
|
||||
);
|
||||
|
||||
if (!shift) {
|
||||
//if shiftnr = 1, notes = "Докарва" + event.transport
|
||||
//if shiftnr = 8, notes = "Взема" + event.transport
|
||||
|
||||
|
||||
let note = event.shiftNr === 1 ? "Докарва количка от Люлин - " + event.transport :
|
||||
event.shiftNr === 6 ? "Прибира количка в Люлин - " + event.transport : "";
|
||||
const shiftEntity = await prisma.shift.create({
|
||||
data: {
|
||||
name: event.dayOfWeek + " " + event.dayOfMonth + ", " + start.toLocaleTimeString() + " - " + end.toLocaleTimeString(),
|
||||
startTime: start,
|
||||
endTime: end,
|
||||
notes: note,
|
||||
cartEvent: {
|
||||
connect: {
|
||||
id: cartEvent.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
shift = shiftEntity;
|
||||
console.log(`Created shift with ID ${shiftEntity.id} for cart event with ID ${cartEvent.id} on ${date} from ${start.toLocaleTimeString()} to ${end.toLocaleTimeString()}`);
|
||||
|
||||
}
|
||||
|
||||
for (const nameOrFamily of event.names) {
|
||||
for (const name of common.separateFamilyMemberNames2(nameOrFamily)) {
|
||||
var publisher = null
|
||||
const pubs = await data.findPublisher(name, null, "id,email,firstName,lastName", true);
|
||||
publisher = pubs[0];
|
||||
|
||||
|
||||
if (!publisher) {
|
||||
const fuzzyPublisher = common.fuzzySearch(publishers, name);
|
||||
|
||||
if (fuzzyPublisher) {
|
||||
console.log(
|
||||
`Found publisher '${fuzzyPublisher.firstName} ${fuzzyPublisher.lastName}' through fuzzy search for '${name}'`
|
||||
);
|
||||
publisher = fuzzyPublisher;
|
||||
} else {
|
||||
console.warn(`NO publisher found! Could not find publisher with name '${name}'. Creating new publisher from known info.`);
|
||||
//continue;
|
||||
try {
|
||||
let firstname = name.substring(0, name.lastIndexOf(" ")).trim();
|
||||
let lastname = name.substring(name.lastIndexOf(" ") + 1).trim();
|
||||
// Remove the last letter if it is "а" or "и"
|
||||
// if (lastname.endsWith('а') || lastname.endsWith('и')) {
|
||||
if (lastname.endsWith('и')) {
|
||||
lastname = lastname.slice(0, -1);
|
||||
}
|
||||
//if any name is empty, skip this publisher
|
||||
if (firstname == "" || lastname == "") {
|
||||
console.warn(`NO publisher found! Could not find publisher with name '${name}', but we need both first and last name. Skipping this publisher.`);
|
||||
continue;
|
||||
}
|
||||
//var name = names[i].trim();
|
||||
// //cut last letter of name if it is "a" or "и" (bulgarian feminine ending)
|
||||
// if (name.endsWith("a") || name.endsWith("и")) {
|
||||
// name = name.substring(0, name.length - 1);
|
||||
// }
|
||||
|
||||
var manualPub = {
|
||||
email: name.toLowerCase().replace(/ /g, "."), // + "@gmail.com"
|
||||
firstName: firstname,
|
||||
lastName: lastname,
|
||||
isactive: true,
|
||||
isImported: true,
|
||||
// role: "EXTERNAL",
|
||||
};
|
||||
publisher = await prisma.publisher.create({ data: manualPub });
|
||||
console.log(`Created publisher with ID ${publisher.id} for name '${name}'`);
|
||||
|
||||
// create availability with the same date as the event.
|
||||
//ToDo: add parameter to control if we want to create availability for each event. can be done whe we import previous shifts.
|
||||
// if (createAvailabilities) {
|
||||
// const dayofWeek = common.getDayOfWeekNameEnEnum(date);
|
||||
// const availability = await prisma.availability.create({
|
||||
// data: {
|
||||
// publisherId: publisher.id,
|
||||
// //date: date,
|
||||
// dayofweek: dayofWeek,
|
||||
// startTime: startTime,
|
||||
// endTime: endTime,
|
||||
// name: `от предишен график, ${publisher.firstName} ${publisher.lastName}`,
|
||||
// isFromPreviousAssignment: true,
|
||||
// isactive: true,
|
||||
// },
|
||||
// });
|
||||
// console.log(`Created WEEKLY availability with ID ${availability.id} for date '${date.toDateString()}' and publisher '${publisher.firstName} ${publisher.lastName}'`);
|
||||
// }
|
||||
// const personResponse = await axiosInstance.post("/publishers", manualPub);
|
||||
// let personId = personResponse.data.id;
|
||||
|
||||
} catch (e) {
|
||||
console.error(`shiftCache: error adding MANUAL publisher to the system (${manualPub.email} ${manualPub.firstName} ${manualPub.lastName}): ` + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (location != null && publisher != null && shift != null) {
|
||||
const assignment = await prisma.assignment.create({
|
||||
data: {
|
||||
//publisherId: publisher.id,
|
||||
// shiftId: shift.id,
|
||||
publisher: {
|
||||
connect: {
|
||||
id: publisher.id,
|
||||
},
|
||||
},
|
||||
shift: {
|
||||
connect: {
|
||||
id: shift.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
//ToDo: fix findPublisherAvailability and creation of availabilities
|
||||
// check if there is an availability for this publisher on this date, and if not, create one
|
||||
// const availability = await data.findPublisherAvailability(publisher.id, start);
|
||||
// if (!availability && createAvailabilities) {
|
||||
// const dayofWeek = common.getDayOfWeekNameEnEnum(date);
|
||||
// const availability = await prisma.availability.create({
|
||||
// data: {
|
||||
// publisherId: publisher.id,
|
||||
// //date: date,
|
||||
// dayofweek: dayofWeek,
|
||||
// //weekOfMonth: common.getWeekOfMonth(date),
|
||||
// startTime: start,
|
||||
// endTime: end,
|
||||
// name: `от предишен график, ${publisher.firstName} ${publisher.lastName}`,
|
||||
// isFromPreviousAssignment: true,
|
||||
// },
|
||||
// });
|
||||
// console.log(`Created WEEKLY availability with ID ${availability.id} for date '${date.toDateString()}' and publisher '${publisher.firstName} ${publisher.lastName}'`);
|
||||
// }
|
||||
|
||||
console.log(`Created assignment with ID ${assignment.id} for date '${date.toDateString()}' and location '${event.placeOfEvent}'. publisher: ${publisher.firstName} ${publisher.lastName}}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
console.log("Done");
|
||||
}
|
||||
|
||||
//We used GPT to generate the file so far - day by day, by copy-pasting it in the chat. See PROMPTS.md
|
||||
exports.ReadDocxFileForMonth = async function (filePath, buffer, month, year, progressCallback, createAvailabilities) {
|
||||
try {
|
||||
const JSZip = require("jszip");
|
||||
const zip = new JSZip();
|
||||
//if filepath is not null read it. otherwise use buffer
|
||||
|
||||
if (filePath != null) {
|
||||
buffer = await fs.readFileSync(filePath);
|
||||
}
|
||||
const zipFile = await zip.loadAsync(buffer);
|
||||
const documentXml = await zipFile.file("word/document.xml").async("string");
|
||||
const xml2js = require("xml2js");
|
||||
const parser = new xml2js.Parser({
|
||||
explicitArray: false,
|
||||
ignoreAttrs: true,
|
||||
});
|
||||
const json = await parser.parseStringPromise(documentXml);
|
||||
//const tableData = parsedXml['w:document']['w:body']['w:tbl']['w:tr'][1]['w:tc']['w:p']['w:r']['w:t'];
|
||||
|
||||
// addParentReferences(json);
|
||||
// const xmlJs = require('xml-js');
|
||||
// const jsonstring = xmlJs.xml2json(json, { compact: true });
|
||||
|
||||
const cleanedJsonObj = common.jsonRemoveEmptyNodes(json);
|
||||
|
||||
//let filename = `График source ${year}-${month}.json`;
|
||||
//fs.writeFileSync("./content/temp/" + filename, JSON.stringify(cleanedJsonObj)); //initial json source for previous shifts
|
||||
|
||||
const extractedData = extractData(cleanedJsonObj, month, year);
|
||||
//console.log(extractedData);
|
||||
|
||||
//modify the file
|
||||
// try {
|
||||
// let filename = `График ${year}-${month}.json`;
|
||||
// fs.writeFileSync("./content/temp/" + filename, JSON.stringify(extractedData));
|
||||
// } catch (e) {
|
||||
// console.log(e);
|
||||
// }
|
||||
await exports.processEvents(extractedData, year, month, progressCallback, createAvailabilities);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
const weekNames = [
|
||||
"Понеделник",
|
||||
"Вторник",
|
||||
"Сряда",
|
||||
"Четвъртък",
|
||||
"Петък",
|
||||
"Събота",
|
||||
"Неделя",
|
||||
];
|
||||
|
||||
function findWeekNameNodes(obj, path = []) {
|
||||
let result = [];
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
const newPath = path.slice();
|
||||
newPath.push(i);
|
||||
result = result.concat(findWeekNameNodes(obj[i], newPath));
|
||||
}
|
||||
} else if (typeof obj === "object") {
|
||||
for (const key in obj) {
|
||||
const newPath = path.slice();
|
||||
newPath.push(key);
|
||||
result = result.concat(findWeekNameNodes(obj[key], newPath));
|
||||
}
|
||||
} else if (typeof obj === "string") {
|
||||
const matches = obj.match(/(\S+) (\d+)/);
|
||||
if (matches && weekNames.includes(matches[1])) {
|
||||
result.push({ weekName: matches[1], dayOfMonth: matches[2], path });
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function findShifts(node) {
|
||||
if (node === null || typeof node !== "object") return null;
|
||||
|
||||
if (node.hasOwnProperty("w:tbl")) {
|
||||
return node["w:tbl"];
|
||||
} else {
|
||||
return findShifts(node._parent);
|
||||
}
|
||||
}
|
||||
|
||||
function extractData(parsedJson, month, year) {
|
||||
const weekNameNodes = findWeekNameNodes(parsedJson);
|
||||
const data = [];
|
||||
let lastDay = 0;
|
||||
let monthOverflow = false;
|
||||
|
||||
|
||||
for (const node of weekNameNodes) {
|
||||
const { weekName, dayOfMonth, path } = node;
|
||||
let currentNode = parsedJson;
|
||||
let baseNode = null;
|
||||
let parentNode = null;
|
||||
const dom = parseInt(dayOfMonth);
|
||||
|
||||
if (lastDay > dom) {
|
||||
monthOverflow = true;
|
||||
}
|
||||
lastDay = dom;
|
||||
let date = new Date(year, month - (monthOverflow ? 0 : 1), dom);
|
||||
|
||||
for (const key of path) {
|
||||
if (currentNode[key] && currentNode[key]["w:tc"]) {
|
||||
parentNode = currentNode;
|
||||
baseNode = currentNode[key];
|
||||
}
|
||||
|
||||
currentNode = currentNode[key];
|
||||
}
|
||||
// for (const key of path) {
|
||||
// parentNode = currentNode;
|
||||
// currentNode = currentNode[key];
|
||||
// }
|
||||
console.log("Processing " + weekName + " " + dayOfMonth + " " + CON.monthNamesBG[date.getMonth()]);
|
||||
const dailyData = extractDataForDay(parentNode, weekName, date);
|
||||
|
||||
dailyData.forEach((item) => {
|
||||
if (!data.some((existingItem) =>
|
||||
existingItem.date === item.date &&
|
||||
existingItem.shiftNr === item.shiftNr &&
|
||||
existingItem.dayOfMonth === item.dayOfMonth
|
||||
)) {
|
||||
data.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function extractDataForDay(weeknameNode, weekName, date) {
|
||||
let result = [];
|
||||
weekName = weekName.split(" ")[0];
|
||||
let placeOfEvent = weeknameNode[0]["w:tc"]["w:p"][1]["w:r"]["w:t"] ?? weeknameNode[0]["w:tc"]["w:p"][1]["w:r"]["0"]["w:t"];
|
||||
|
||||
let names = [];
|
||||
let shiftNr = 0;
|
||||
let tbl = weeknameNode;
|
||||
|
||||
for (const trKey in tbl) {
|
||||
try {
|
||||
const weekNameNodes = findWeekNameNodes(tbl[trKey]);
|
||||
if (weekNameNodes.length > 0) {
|
||||
if (names && names.length > 0) {
|
||||
shiftNr = 0;
|
||||
}
|
||||
dayOfMonth = date.getDate(); //tbl[trKey]["w:tc"]["w:p"][0]["w:r"]["w:t"].match(/(\d+)/)[1];
|
||||
weekName = weekName; // tbl[trKey]["w:tc"]["w:p"][0]["w:r"]["w:t"].match(/(\S+) (\d+)/)[1];
|
||||
placeOfEvent = placeOfEvent.trim();//tbl[trKey]["w:tc"]["w:p"][1]["w:r"]["w:t"];
|
||||
continue;
|
||||
}
|
||||
const tr = tbl[trKey];
|
||||
console.log("Processin row: " + JSON.stringify(tr));
|
||||
|
||||
let time = tr["w:tc"]?.[1]?.["w:p"]?.["w:r"]?.["w:t"] ?? tr["w:tc"]?.[1]?.["w:p"]?.[0]?.["w:r"]?.["w:t"];
|
||||
let transport = tr["w:tc"]?.[3]?.["w:p"]?.["w:r"]?.[1]?.["w:t"];
|
||||
|
||||
|
||||
let namesPath = ["w:tc", 2, "w:p"];
|
||||
try {
|
||||
names = [getTextContent(safelyAccess(tr, namesPath))].join("").trim();
|
||||
} catch (e) {
|
||||
console.log("try to parse names:" + names + "; " + e + " " + JSON.stringify(tr["w:tc"] + " " + trKey) + e.stack);
|
||||
}
|
||||
//if starts with "Докарва" or empty - try the first cell instead of second
|
||||
if (names.startsWith("Докарва") || names.startsWith("Прибира ") || names === "") {
|
||||
transport = names;
|
||||
time = getTextContent(safelyAccess(tr, ["w:tc", 0, "w:p"]));
|
||||
namesPath = ["w:tc", 1, "w:p"];
|
||||
try {
|
||||
names = [getTextContent(safelyAccess(tr, namesPath))].join("").trim();
|
||||
} catch (e) {
|
||||
console.log("try to parse names:" + names + "; " + e + " " + JSON.stringify(tr["w:tc"] + " " + trKey) + e.stack);
|
||||
}
|
||||
}
|
||||
|
||||
names = names.split(",").map((name) => name.trim()).filter((name) => name !== "");
|
||||
|
||||
shiftNr++;
|
||||
result.push({
|
||||
date,
|
||||
dayOfWeek: weekName,
|
||||
dayOfMonth: date.getDate(),
|
||||
placeOfEvent,
|
||||
shiftNr,
|
||||
time,
|
||||
names,
|
||||
transport,
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("failed extracting data from node " + trKey + ": " + e + ": " + e.stack);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function safelyAccess(obj, path) {
|
||||
return path.reduce((acc, key) => (acc && key in acc) ? acc[key] : undefined, obj);
|
||||
}
|
||||
const getTextContent = (obj) => {
|
||||
let textContent = '';
|
||||
|
||||
const traverse = (node) => {
|
||||
if (typeof node === 'string') {
|
||||
textContent += node;
|
||||
} else if (Array.isArray(node)) {
|
||||
node.forEach((child) => traverse(child));
|
||||
} else if (typeof node === 'object') {
|
||||
Object.values(node).forEach((child) => traverse(child));
|
||||
}
|
||||
};
|
||||
|
||||
traverse(obj);
|
||||
return textContent;
|
||||
};
|
||||
|
||||
// ImportSchedule("./content/sources/march.json", 3);
|
||||
//
|
||||
function GenerateFlatJsonFile() {
|
||||
let data = JSON.parse(fs.readFileSync("./content/sources/test.json", "utf8"));
|
||||
let data_flat = transformJsonToFlat(data, 3);
|
||||
fs.writeFileSync(
|
||||
"./content/sources/march_flat.json",
|
||||
JSON.stringify(data_flat)
|
||||
);
|
||||
}
|
||||
|
||||
function transformJsonToFlat(inputJson, month) {
|
||||
const output = [];
|
||||
let dayNr = 0;
|
||||
|
||||
inputJson.events.forEach((event) => {
|
||||
const date = new Date(2023, month - 1, event.dayOfMonth + 1);
|
||||
dayNr++;
|
||||
let shiftNr = 1;
|
||||
event.shifts.forEach((shift) => {
|
||||
const shiftNames = shift.names.split(",").map((name) => name.trim());
|
||||
|
||||
// if (shift.transport !== null) {
|
||||
// shiftNames.push(shift.transport);
|
||||
// }
|
||||
|
||||
output.push({
|
||||
date: common.getISODateOnly(date),
|
||||
dayOfWeek: event.dayOfWeek,
|
||||
dayNr,
|
||||
shiftNr: shiftNr++,
|
||||
time: shift.time,
|
||||
names: shiftNames,
|
||||
transport: shift.transport,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
Reference in New Issue
Block a user