Skip to content

Commit

Permalink
in/export shifts
Browse files Browse the repository at this point in the history
  • Loading branch information
kaplonpaulina committed Mar 20, 2021
1 parent 5f1dc82 commit d3d24fd
Show file tree
Hide file tree
Showing 5 changed files with 276 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const expectedSchedule: ScheduleDataModel = {
//#endregion

describe("Schedule parser", () => {
const scheduleParser = new ScheduleParser(10, 2020, exampleData, []);
const scheduleParser = new ScheduleParser(10, 2020, exampleData, [], []);
const result = scheduleParser.schedule.getDataModel();
//todo update test
// it("check if workerType was parsed correctly ", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { useEffect, useState } from "react";
import Excel from "exceljs";
import Excel, { FillPattern } from "exceljs";
import { MonthDataModel } from "../../../../common-models/schedule-data.model";
import { InputFileErrorCode, ScheduleError } from "../../../../common-models/schedule-error.model";
import { ScheduleParser } from "../../../../logic/schedule-parser/schedule.parser";
Expand All @@ -13,6 +13,7 @@ import { fromBuffer } from "file-type/browser";
import { useNotification } from "../../../common-components/notification/notification.context";
import { cropScheduleDMToMonthDM } from "../../../../logic/schedule-container-convertion/schedule-container-convertion";
import {
SHIFTS_WORKSHEET_NAME,
WORKERS_WORKSHEET_NAME,
WORKSHEET_NAME,
} from "../../../../logic/schedule-exporter/schedule-export.logic";
Expand Down Expand Up @@ -111,6 +112,7 @@ export function useScheduleConverter(): UseScheduleConverterOutput {
cellsToAvoid.some((cellToAvoid) => setItem.trim().toLowerCase().includes(cellToAvoid))
)) ||
Array.from(rowValuesSet).some((setItem) => setItem.trim().toLowerCase().includes("grafik")) ||
Array.from(rowValuesSet).some((setItem) => setItem.trim().toLowerCase().includes("nazwa")) ||
(rowValuesSet.size === 1 && rowValuesSet.has(""))
);
}
Expand All @@ -136,6 +138,31 @@ export function useScheduleConverter(): UseScheduleConverterOutput {
return workers;
}

function extractShifts(workbook): Array<Array<string>> {
const shifts = Array<Array<string>>();
const shiftsWorkSheet = workbook.getWorksheet(SHIFTS_WORKSHEET_NAME);

if (shiftsWorkSheet) {
shiftsWorkSheet.eachRow(false, (row) => {
const rowValues = Array<string>();

let iter;
for (iter = 1; iter <= 5; iter++) {
rowValues.push(row.getCell(iter).text.trim().toLowerCase());
}

rowValues.push(
(row.getCell(iter).style.fill as FillPattern).fgColor?.argb?.toString() ?? ""
);

if (!isEmpty(rowValues)) {
shifts.push(rowValues);
}
});
}
return shifts;
}

function extractSchedule(workbook): Array<Array<Array<string>>> {
const scheduleWorkSheet = workbook.getWorksheet(WORKSHEET_NAME);
if (scheduleWorkSheet.rowCount === 0) {
Expand Down Expand Up @@ -183,15 +210,17 @@ export function useScheduleConverter(): UseScheduleConverterOutput {
function readFileContent(workbook): void {
const scheduleArray = extractSchedule(workbook);
const workersArray = extractWorkers(workbook);
const shiftsArray = extractShifts(workbook);

if (Object.keys(scheduleArray).length !== 0) {
const parser = new ScheduleParser(month, year, scheduleArray, workersArray);
const parser = new ScheduleParser(month, year, scheduleArray, workersArray, shiftsArray);

setScheduleErrors([
...parser._parseErrors,
...parser.sections.Metadata.errors,
...parser.sections.FoundationInfo.errors,
...parser.workersInfo.errors,
...parser.shiftsInfo.errors,
]);
setMonthModel(cropScheduleDMToMonthDM(parser.schedule.getDataModel()));

Expand Down
77 changes: 75 additions & 2 deletions src/logic/schedule-exporter/schedule-export.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ const EMPTY_ROW = Array(100).fill("");

export const WORKSHEET_NAME = "grafik";
export const WORKERS_WORKSHEET_NAME = "pracownicy";
export const SHIFTS_WORKSHEET_NAME = "zmiany";

export interface ScheduleExportLogicOptions {
scheduleModel: MonthDataModel;
primaryScheduleModel?: PrimaryMonthRevisionDataModel;
overtimeExport?: boolean;
extraWorkersExport?: boolean;
}

export class ScheduleExportLogic {
private scheduleModel: MonthDataModel;
private primaryScheduleModel?: PrimaryMonthRevisionDataModel;
Expand Down Expand Up @@ -65,10 +67,16 @@ export class ScheduleExportLogic {
}

public createWorkbook(revisionType: RevisionType): [string, xlsx.Workbook] {
const [workbook, scheduleWorkSheet, workersWorkSheet] = ScheduleExportLogic.createWorkArea();
const [
workbook,
scheduleWorkSheet,
workersWorkSheet,
shiftsWorkSheet,
] = ScheduleExportLogic.createWorkArea();

this.setScheduleWorkSheet(scheduleWorkSheet);
this.setWorkersWorkSheet(workersWorkSheet);
this.setShiftsWorkSheet(shiftsWorkSheet);

const workbookName = FileHelper.createMonthFilename(this.scheduleModel, revisionType);
return [workbookName, workbook];
Expand Down Expand Up @@ -171,7 +179,48 @@ export class ScheduleExportLogic {
workSheet.getRow(1).font = { bold: true };
}

private static createWorkArea(): [xlsx.Workbook, xlsx.Worksheet, xlsx.Worksheet] {
private setShiftsWorkSheet(workSheet: xlsx.Worksheet): void {
workSheet.pageSetup.showGridLines = true;
workSheet.pageSetup.fitToPage = true;
workSheet.pageSetup.fitToHeight = 1;
workSheet.pageSetup.fitToWidth = 1;
workSheet.pageSetup.horizontalCentered = true;

const shiftsInfoArray = ScheduleExportLogic.createShiftsInfoSection(this.scheduleModel);

const colLens = shiftsInfoArray[0].map((_, colIndex) =>
Math.max(...shiftsInfoArray.map((row) => row[colIndex].toString().length))
);

workSheet.addRows(shiftsInfoArray);

colLens.forEach((len, id) => {
workSheet.getColumn(id + 1).width = len + 4;
});

let iter;
for (iter = 3; iter <= workSheet.rowCount; iter++) {
const cell = workSheet.getCell(iter, 6);
cell.style.fill = {
type: "pattern",
pattern: "solid",
fgColor: { argb: cell.text },
};
cell.value = "";
}

workSheet.getColumn(1).alignment = { vertical: "middle", horizontal: "left" };
workSheet.getColumn(2).alignment = { vertical: "middle", horizontal: "center" };
workSheet.getColumn(3).alignment = { vertical: "middle", horizontal: "center" };
workSheet.getColumn(4).alignment = { vertical: "middle", horizontal: "center" };
workSheet.getColumn(5).alignment = { vertical: "middle", horizontal: "center" };
workSheet.getColumn(6).alignment = { vertical: "middle", horizontal: "center" };

workSheet.getRow(1).alignment = { vertical: "middle", horizontal: "center" };
workSheet.getRow(1).font = { bold: true };
}

private static createWorkArea(): [xlsx.Workbook, xlsx.Worksheet, xlsx.Worksheet, xlsx.Worksheet] {
const workbook = new xlsx.Workbook();
return [
workbook,
Expand All @@ -183,6 +232,10 @@ export class ScheduleExportLogic {
pageSetup: { paperSize: 9, orientation: "landscape" },
properties: { defaultColWidth: 5 },
}),
workbook.addWorksheet(SHIFTS_WORKSHEET_NAME, {
pageSetup: { paperSize: 9, orientation: "landscape" },
properties: { defaultColWidth: 5 },
}),
];
}

Expand Down Expand Up @@ -383,6 +436,26 @@ export class ScheduleExportLogic {
return [...workers];
}

private static createShiftsInfoSection(scheduleModel: MonthDataModel): (string | number)[][] {
const names = Object.values(scheduleModel.shift_types);

const workers: (string | number)[][] = [];

workers.push(["Nazwa", "Skrót", "Od", "Do", "Zmiana pracująca", "Kolor"]);
workers.push(EMPTY_ROW);
names.forEach((name) =>
workers.push([
name.name,
name.code,
name.from,
name.to,
name.isWorkingShift === true ? "TAK" : "NIE",
name.color,
])
);
return [...workers];
}

private static createWorkHoursInfoHeader(startIndex: number): string[] {
const header = Array(startIndex + 3).fill("");

Expand Down
11 changes: 10 additions & 1 deletion src/logic/schedule-parser/schedule.parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,26 @@ import { FoundationInfoHeaders, FoundationInfoParser } from "./foundation-info.p
import { FoundationInfoOptions } from "../providers/foundation-info-provider.model";
import { ExtraWorkersInfoParser } from "./extra-workers-info.parser";
import { WorkersInfoParser } from "./workers-info.parser";
import { ShiftsProperInfoParser } from "./shifts-proper-info.parser";

export class ScheduleParser implements ScheduleProvider {
readonly sections: Sections;
readonly workersInfo: WorkersInfoParser;
readonly shiftsInfo: ShiftsProperInfoParser;
readonly schedule: Schedule;
readonly isAutoGenerated: boolean;
_parseErrors: ScheduleError[] = [];

constructor(readonly month, readonly year, rawSchedule: string[][][], workersInfo: string[][]) {
constructor(
readonly month,
readonly year,
rawSchedule: string[][][],
workersInfo: string[][],
shiftsInfo: string[][]
) {
this.sections = this.parseSections(rawSchedule);
this.workersInfo = new WorkersInfoParser(workersInfo);
this.shiftsInfo = new ShiftsProperInfoParser(shiftsInfo);
this.isAutoGenerated = false;
this.schedule = new Schedule(this);
}
Expand Down
159 changes: 159 additions & 0 deletions src/logic/schedule-parser/shifts-proper-info.parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { InputFileErrorCode, ScheduleError } from "../../common-models/schedule-error.model";
import { Shift } from "../../common-models/shift-info.model";
import { AcronymGenerator } from "../../helpers/acronym-generator.helper";

export const DEFAULT_SHIFT_NAME = "Nowa zmiana";
export const DEFAULT_FROM = 0;
export const DEFAULT_TO = 24;
export const DEFAULT_IS_WORKING = false;
export const DEFAULT_COLOR = { name: "żółty", value: "FFD100" };

export class ShiftsProperInfoParser {
private namelessShifts: number;
private _shiftsInfoRows: { [key: string]: Shift } = {};
private _parseErrors: ScheduleError[] = [];

constructor(data: string[][]) {
this.namelessShifts = 0;
data.forEach((a) => {
const worker = this.mapShift(a);
this._shiftsInfoRows[worker.name] = worker;
});
}

public get errors(): ScheduleError[] {
return [...this._parseErrors];
}

public get shiftsDescriptions(): Shift[] {
return Object.values(this._shiftsInfoRows);
}

private logLoadFileError(msg: string): void {
this._parseErrors.push({
kind: InputFileErrorCode.LOAD_FILE_ERROR,
message: msg,
});
}

private parseShiftName(shiftRow: string[]): string {
if (shiftRow[0] && shiftRow[0] !== "") {
return shiftRow[0];
}

const generatedName = this.generateDefaultName();
this.logLoadFileError("Nie ustawiono nazwy dla zmiany. Ustawiono: " + generatedName);
return generatedName;
}

private generateDefaultName(): string {
return DEFAULT_SHIFT_NAME + ++this.namelessShifts;
}

private parseShiftCode(shiftRow: string[], name: string): string {
if (shiftRow[1]) {
return shiftRow[1];
} else {
const generatedCode = AcronymGenerator.generate(name);

this.logLoadFileError(
"Nie ustawiono skrótu dla zmiany: " + name + ". Ustawiono: " + generatedCode
);
return generatedCode;
}
}

private parseShiftFrom(shiftRow: string[], name: string): number {
if (shiftRow[2]) {
const number = parseInt(shiftRow[2].trim());
if (isNaN(number) || number < 0 || number > 24) {
this.logLoadFileError(
"Nieoczekiwana wartość dla początku zmiany: " + name + ". Ustawiono: " + DEFAULT_FROM
);
} else {
return number;
}
} else {
this.logLoadFileError(
"Nie ustawiono początku zmiany: " + name + ". Ustawiono: " + DEFAULT_FROM
);
}
return DEFAULT_FROM;
}

private parseShiftTo(shiftRow: string[], name: string): number {
if (shiftRow[3]) {
const number = parseInt(shiftRow[3].trim());
if (isNaN(number) || number < 0 || number > 24) {
this.logLoadFileError(
"Nieoczekiwana wartość dla początku zmiany: " + name + ". Ustawiono: " + DEFAULT_TO
);
} else {
return number;
}
} else {
this.logLoadFileError(
"Nie ustawiono początku zmiany: " + name + ". Ustawiono: " + DEFAULT_TO
);
}
return DEFAULT_TO;
}

private parseShiftIsWorking(shiftRow: string[], name: string): boolean {
if (shiftRow[4]) {
const isWorkingShift = shiftRow[4].trim().toLowerCase();
if (isWorkingShift === "tak") {
return true;
} else if (isWorkingShift === "nie") {
return false;
} else {
this.logLoadFileError(
"Nieoczekiwana wartość dla rodzaju zmiany: " +
name +
". Ustawiono: " +
(DEFAULT_IS_WORKING ? "TAK" : "NIE")
);
}
} else {
this.logLoadFileError(
"Nie ustawiono czy zmiana: " +
name +
" jest pracująca. Ustawiono: " +
(DEFAULT_IS_WORKING ? "TAK" : "NIE")
);
}
return DEFAULT_IS_WORKING;
}

private parseShiftColor(shiftRow: string[], name: string): string {
if (shiftRow[5]) {
return shiftRow[5];
} else {
this.logLoadFileError(
"Nie ustawiono koloru dla zmiany: " + name + ". Ustawiono: " + DEFAULT_COLOR.name
);
return DEFAULT_COLOR.value;
}
}

private mapShift(row: string[]): Shift {
const name = this.parseShiftName(row);
const code = this.parseShiftCode(row, name);
const from = this.parseShiftFrom(row, name);
const to = this.parseShiftTo(row, name);
const isWorkingShift = this.parseShiftIsWorking(row, name);
const color = this.parseShiftColor(row, name);

return {
name: name,
code: code,
from: from,
to: to,
isWorkingShift: isWorkingShift,
color: color,
};
}
}

0 comments on commit d3d24fd

Please sign in to comment.