initial commit - code moved to separate repo

This commit is contained in:
Dobromir Popov
2024-02-22 04:19:38 +02:00
commit 560d503219
240 changed files with 105125 additions and 0 deletions

822
src/helpers/excel.js Normal file
View 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;
}