-
Notifications
You must be signed in to change notification settings - Fork 0
/
write_excel.ts
129 lines (113 loc) · 4.18 KB
/
write_excel.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import type pl from "polars";
import ExcelJS from "@tinkie101/exceljs-wrapper";
import type { RowData, TableStyle } from "./types.ts";
/**
* Writes one or more DataFrames to an Excel file, each in its own worksheet.
*
* @param df - The DataFrame or array of DataFrames to write to the Excel file.
* @param filePath - The path where the Excel file will be saved.
* @param options - Optional settings for writing the Excel file.
* @param options.sheetName - The name(s) of the sheets in the Excel file. Defaults to ["Sheet1", "Sheet2", ...].
* @param options.includeHeader - Whether to include the DataFrame's column headers in the Excel file. Defaults to true.
* @param options.autofitColumns - Whether to auto-fit the columns based on their content. Defaults to true.
* @param options.tableStyle - The style to apply to the tables in the Excel file.
* @param options.header - The header to add to the top of each page in the Excel file.
* @param options.footer - The footer to add to the bottom of each page in the Excel file.
* @throws Will throw an error if all the DataFrames are empty.
* @returns A promise that resolves when the Excel file has been written.
*/
export async function writeExcel(
df: pl.DataFrame | pl.DataFrame[],
filePath: string,
options: {
sheetName?: string | string[];
includeHeader?: boolean;
autofitColumns?: boolean;
tableStyle?: TableStyle;
header?: string;
footer?: string;
} = {},
): Promise<void> {
const {
sheetName = "Sheet1",
includeHeader = true,
autofitColumns = true,
tableStyle,
header,
footer,
} = options;
const dataframes = Array.isArray(df) ? df : [df];
const sheetNames = Array.isArray(sheetName) ? sheetName : [sheetName];
if (sheetNames.length < dataframes.length) {
throw new Error("Not enough sheet names provided for the DataFrames.");
}
// Check if all DataFrames are empty
const allEmpty = dataframes.every((df) => df.height === 0);
if (allEmpty) {
if (dataframes.length === 1) {
throw new Error("The DataFrame is empty. Nothing to write.");
} else {
throw new Error("All provided DataFrames are empty. Nothing to write.");
}
}
const workbook = new ExcelJS.Workbook();
for (let i = 0; i < dataframes.length; i++) {
const currentDf = dataframes[i];
const currentSheetName = sheetNames[i] || `Sheet${i + 1}`;
const rows: RowData[] = currentDf.toRecords();
// Skip writing empty DataFrames but don't throw
if (rows.length === 0) {
console.warn(
`DataFrame at index ${i} is empty. Skipping this worksheet.`,
);
continue;
}
const worksheet = workbook.addWorksheet(currentSheetName);
// Add headers if needed
const headers = includeHeader ? Object.keys(rows[0]) : [];
if (includeHeader) worksheet.addRow(headers);
// Add data rows
rows.forEach((row) => {
const values = headers.map((header) => row[header] ?? null);
worksheet.addRow(values);
});
// Apply table style if provided
if (tableStyle && includeHeader) {
const tableRange = {
topLeft: worksheet.getCell(1, 1),
bottomRight: worksheet.getCell(rows.length + 1, headers.length),
};
worksheet.addTable({
name: `Table_${currentSheetName}`,
ref: tableRange.topLeft.address,
headerRow: true,
style: { theme: tableStyle },
columns: headers.map((header) => ({ name: header })),
rows: rows.map((row) => headers.map((header) => row[header] ?? null)),
});
}
// Auto-fit columns
if (autofitColumns) {
worksheet.columns.forEach((column) => {
if (column.values) {
column.width = Math.max(
...column.values
.slice(1) // Skip the metadata slot
.map((value) => (value ? value.toString().length : 10)),
);
} else {
column.width = 10; // Default width
}
});
}
if (header) {
worksheet.headerFooter.oddHeader = header;
worksheet.headerFooter.evenHeader = header;
}
if (footer) {
worksheet.headerFooter.oddFooter = footer;
worksheet.headerFooter.evenFooter = footer;
}
}
await workbook.xlsx.writeFile(filePath);
}